2014 Multi-University Training Contest 5 F題。
題目大意:給定一顆n個節點的樹,q個查詢,每次查詢輸入兩個端點(u,v),代表樹上一條從u->v的路徑,求刪掉這條路徑上的點後,剩下的點中編號最小的點。
題目分析:可以這樣想象:(1)我們以1這個節點爲根,刪掉根節點後形成若干個聯通分量,很顯然若路徑中的兩個端點在刪掉根節點後還在同一個聯通分量中,那麼所求結果必然爲1。這個可在預處理後O(1)實現(2)若兩個端點不在同一個聯通分量中,那麼必然經過根節點,此時若純暴力則時間複雜度可達O(n*q)之大,題目數據量非常大,必然死掉。 官方的題解給了做題的方向,可以用樹形DP的思想,預處理求出每個節點的f[]值,f[u]代表從u到他所屬的根節點(刪除1之後的)除去路徑上的點後編號最小的點值。
如下圖所示,所求的值f[2]=4,f[3]=7,f[4]=5,f[5]=6,f[7]=8,f[8]=7,f[9]=8;
2所對應的根節點爲2,f[2]=min(與2同屬於一個聯通分量且不在2到根節點的鏈上的,2以下的節點中編號最小的);
求出所有的f[]值後,還需知道各個聯通分量中編號最小的點,因爲當根節點1的兒子節點有多個,即有多個聯通分量時 ,兩個端點分屬兩個聯通分量,最小值可能出現在別的聯通分量中,但f[]值沒有考慮到。 則我們需要保存編號最小的三個聯通分量。
比較繞口,過後補上圖解。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<algorithm>
#include<queue>
struct node
{
int u,parent;
node(){}
node(int x,int y)
{
u=x,parent=y;
}
};
using namespace std;
const int maxn = 1000010;
stack<int >S;
queue<node >Q;
int pp[maxn];
int n,q,ecnt=0;
int next[maxn*2],e[maxn*2],head[maxn];
int cnt[maxn],f[maxn],x[5],y[5],other[maxn];
void addedge(int u,int v)
{
e[ecnt]=v;
next[ecnt]=head[u];
head[u]=ecnt++;
e[ecnt]=u;
next[ecnt]=head[v];
head[v]=ecnt++;
}
void dfs(int u,int fa)
{
f[u]=fa;
S.push(u);
while (!S.empty())
{
u=S.top();
int flag=0;
for (int i=head[u];i!=-1;i=next[i])
{
int v=e[i];
if (!f[v])
{
f[v]=fa;
S.push(v);
flag=1;
break;
}
}
if (!flag)
{
S.pop();
if (!S.empty())
{
int v=u;
u=S.top();
cnt[u]=min(v,cnt[u]);
cnt[u]=min(cnt[u],cnt[v]);
}
}
}
}
void bfs(int u,int parent)
{
Q.push(node(u,parent));
while (!Q.empty())
{
int x1=n+1,x2=n+1,y1=n+1,y2=n+1;
node tem=Q.front();
Q.pop();
u=tem.u;
parent=tem.parent;
for (int i=head[u];i!=-1;i=next[i])
{
int v=e[i];
if (v!=parent)
{
Q.push(node(v,u));
int yy=min(cnt[v],v);
if (yy<y1)
{
y2=y1;
x2=x1;
y1=yy;
x1=v;
}
else if (yy<y2)
{
y2=yy;
x2=v;
}
}
}
for (int i=head[u];i!=-1;i=next[i])
{
int v=e[i];
if (v==u)
continue;
other[v]=other[u];
if (v!=x1)
{
other[v]=min(other[v],y1);
}
else
{
other[v]=min(other[v],y2);
}
}
}
return ;
}
inline void scan(int &n)
{
char cc;
for (; cc = getchar(), cc<'0' || cc>'9';);
n = cc - '0';
for (; cc = getchar(), cc >= '0'&&cc <= '9';)
n = n * 10 + cc - '0';
}
int main()
{
while (scanf("%d%d",&n,&q)!=EOF)
{
int i,j,u,v,prex=0;
ecnt=0;
for (i=1;i<=n;i++)
{
head[i]=-1;
f[i]=0;
cnt[i]=n+1;
other[i]=n+1;
}
for (i=1;i<n;i++)
{
// scanf("%d%d",&u,&v);
scan(u);
scan(v);
addedge(u,v);
}
f[1]=1;
cnt[1]=n+1;
x[0]=x[1]=x[2]=0;
y[0]=y[1]=y[2]=n+1;
for (i=head[1];i!=-1;i=next[i])
{
dfs(e[i],e[i]);
}
for (i=head[1];i!=-1;i=next[i])
{
bfs(e[i],1);
int fa=min(cnt[e[i]],e[i]);
if (fa<y[0])
{
y[2]=y[1];
y[1]=y[0];
y[0]=fa;
x[2]=x[1];
x[1]=x[0];
x[0]=e[i];
}
else if (fa<y[1])
{
y[2]=y[1];
y[1]=fa;
x[2]=x[1];
x[1]=e[i];
}
else if (fa<y[2])
{
y[2]=fa;
x[2]=e[i];
}
}
for (i=2;i<=n;i++)
{
cnt[i]=min(other[i],cnt[i]);
}
for (i=0;i<q;i++)
{
// scanf("%d%d",&u,&v);
scan(u);
scan(v);
u^=prex;
v^=prex;
if (f[u]==f[v]&&(u!=1&&v!=1))
{
printf("1\n");
prex=1;
}
else
{
int tot=min(cnt[u],cnt[v]);
if (tot>y[0]&&(x[0]!=f[u]&&x[0]!=f[v]))
{
tot=y[0];
}
else if (tot>y[1]&&(x[1]!=f[u]&&x[1]!=f[v]))
{
tot=y[1];
}
else if (tot>y[2])
{
tot=y[2];
}
prex=tot;
printf("%d\n",tot);
}
}
}
return 0;
}