CodeForces 825G"Tree Queries"(选根建树)
•参考资料
[1]:CodeForces 825G Educational Round #25 G :建树选根大法+O1大法+iostream解绑了还是慢
•题意
给定一颗包含 n 个节点的树,开始树的所有节点都是白色的;
给出 q 次询问,询问分为1、2两种:
- 将节点 x 涂成黑色。
- 询问节点 x 到所有的黑点节点的简单路径中的标号最小的那个点(包括起点和黑点)
题目保证第一次询问是 1 类型的。
•题解
如果我们随便选取某节点作为根节点,那么询问的时候,我们要找到节点 x 到所有黑色节点的 LCA;
但是这样显然会超时的,所以我们换一种建树方法。
由于第一个询问必然是 1 类型,那么我们就把第一次询问的那个变黑的点作为根节点,看一下这样有什么好处;
定义 $res_i$ 表示节点 i 到根节点(询问1的x)的路径中,标号最小的节点;
首先,我们预处理出所有的 $res$,只需 $DFS$ 一遍即可,时间复杂度 $O(n)$;
接下来,如果剩余的询问全部是 2 类型,那么,对于节点 x 的询问,直接输出 $res_x$ 即可;
但是,如果存在 1 类型的询问呢?
对于新的黑色节点 $u_1,u_2,.....$,在查询节点 x 的时候,除了需要知道节点 x 到根节点路径上标号最小的节点;
同时还需要求出节点 x 到黑色节点 $u_i$ 路径上标号最小的节点;
你会发现,求解节点 x 到黑色节点 $u_i$ 路径上的标号最小的节点等价于求解根节点到黑色节点 $u_i$ 路径上的标号最小的节点;
那这么说的话,我们就可以定义一个变量 $Min$,用来存储新加入的黑色节点到根节点的路径上标号最小的节点信息;
询问的时候,只需输出 $res_x$ 和 $Min$ 的最小值即可;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define INF 0x3f3f3f3f 4 #define mem(a,b) memset(a,b,sizeof(a)) 5 const int maxn=1e6+50; 6 7 int n,q; 8 int num; 9 int head[maxn]; 10 struct Edge 11 { 12 int to; 13 int next; 14 }G[maxn<<1]; 15 void addEdge(int u,int v) 16 { 17 G[num]={v,head[u]}; 18 head[u]=num++; 19 } 20 int res[maxn]; 21 22 void DFS(int u,int f) 23 { 24 res[u]=min(u,res[f]); 25 for(int i=head[u];~i;i=G[i].next) 26 { 27 int v=G[i].to; 28 if(v != f) 29 DFS(v,u); 30 } 31 } 32 void Solve() 33 { 34 mem(res,INF); 35 36 int ans=0; 37 int Min=INF; 38 for(int i=1;i <= q;++i) 39 { 40 int t,z; 41 scanf("%d%d",&t,&z); 42 int x=(z+ans)%n+1; 43 44 if(i == 1) 45 DFS(x,x); 46 else if(t == 1) 47 Min=min(Min,res[x]); 48 else 49 { 50 ans=min(Min,res[x]); 51 printf("%d\n",ans); 52 } 53 } 54 } 55 void Init() 56 { 57 num=0; 58 mem(head,-1); 59 } 60 int main() 61 { 62 Init(); 63 scanf("%d%d",&n,&q); 64 for(int i=1;i < n;++i) 65 { 66 int u,v; 67 scanf("%d%d",&u,&v); 68 addEdge(u,v); 69 addEdge(v,u); 70 } 71 Solve(); 72 73 return 0; 74 }