大体题意:
多组数据,每组数据给你一个树(数的节点最多10W),然后有m(m <= 20W)个操作,每一组操作有 换树的树根,和查询u,v节点的最深公共最先?
思路:
这个题目给了自己很大的教训:模板都是次要的,关键还是怎么发挥模板,关键还是思维!,还需要勤加练习!!
比赛过程中,只用了在线求LCA的模板,但思维太局限了,超时了!请教了学长,感觉比较巧妙!
初始化树根只能初始化一次,剩余的换根操作,只能分类讨论进行比较:
刚开始按题目要求来即可,把树根建立为1.
然后用一个变量root记录根的序号!
对于每一个查询,如果root还是1,直接查即可!
如果不是1,就要讨论了:
先看root 和当前 u,v祖先有没有交集,这里说的交集是lca(root,lca(u,v)) != lca表示root 与u,v祖先没有交集,那么换根后,uv祖先不变!
如果有交集就要对每一种情况讨论:
先说下讨论的依据:
①u,v的祖先 是u或者v的某一个!
②u,v的祖先既不是u也不是v!
讨论第一种情况时:
可以看作u,v是一条链,然后讨论root插在哪里,值是多少即可! 大体看图示:
其实这三个位置的root 返回值是一样的,都是 lca(u,root)
然后在交换以下u,v在讨论一遍即可!
对于第二种情况来说:
同理于情况①,讨论仔细即可!
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 200000 + 10;
int rmq[MAXN<<1];
int d[MAXN];
struct ST{
int mm[MAXN << 1];
int dp[MAXN << 1][20];
void init(int n){
mm[0] = -1;
for (int i = 1; i <= n; ++i){
mm[i] = ((i&(i-1)) == 0 ) ? mm[i-1]+1:mm[i-1];
dp[i][0] = i;
}
for (int j = 1; j <= mm[n]; ++j)
for (int i = 1; i + (1<<j)-1 <= n; ++i)
dp[i][j] = rmq[dp[i][j-1]] < rmq[dp[i+(1<<(j-1))][j-1] ] ? dp[i][j-1] : dp[i+(1<<(j-1))][j-1];
}
int query(int a,int b){
if (a>b) swap(a,b);
int K = mm[b-a+1];
return rmq[dp[a][K]] <= rmq[dp[b-(1<<K)+1][K]] ? dp[a][K] : dp[b-(1<<K)+1 ][K];
}
};
struct Edge{
int to, next;
};
Edge edge[MAXN+2];
int tot,head[MAXN];
int F[MAXN << 1];
int P[MAXN];
int cnt;
ST st;
void init(){
tot = 0;
memset(head,-1,sizeof head);
}
void addedge(int u,int v){
edge[tot].to = v;
edge[tot].next = head[u];
head[u] = tot++;
}
void dfs(int u,int pre,int dep){
d[u] = dep;
F[++cnt] = u;
rmq[cnt] = dep;
P[u] = cnt;
for (int i = head[u]; i != -1; i = edge[i].next){
int v = edge[i].to;
if (v == pre)continue;
dfs(v,u,dep+1);
F[++cnt] = u;
rmq[cnt] = dep;
}
}
void LCA_init(int root,int node_num){
cnt = 0;
dfs(root,root,0);
st.init(2*node_num-1);
}
int query_lca(int u,int v){
return F[st.query(P[u],P[v])];
}
int n;
int solve(int root,int u,int v){
int lca = query_lca(u,v);
int lcau = query_lca(u,root);
int lcav = query_lca(v,root);
int lca2 = query_lca(root,lca);
if (root == 1 || lca2 != lca)return lca;
if (lca == v && lcav == v) return lcau;
else if (lca == u && lcau == u) return lcav;
if (lcav == lca) return lcau;
if (lcau == lca) return lcav;
}
int main(){
freopen("dynamic.in","r",stdin);
freopen("dynamic.out","w",stdout);
while(scanf("%d",&n) == 1 && n){
init();
for (int i = 0; i < n-1; ++i){
int u,v;
scanf("%d %d",&u, &v);
addedge(u,v);
addedge(v,u);
}
int m;
scanf("%d",&m);
char cmd[3];
int root = 1;
LCA_init(root,n);
for (int i = 0; i < m; ++i){
scanf("%s",cmd);
if (cmd[0] == '!'){
scanf("%d",&root);
}
else {
int u,v;
scanf("%d %d",&u, &v);
printf("%d\n",solve(root,u,v));
}
}
}
return 0;
}