传送门ios
•参考资料
[1]:CodeForces 825G Educational Round #25 G :建树选根大法+O1大法+iostream解绑了仍是慢c++
•题意
给定一颗包含 n 个节点的树,开始树的全部节点都是白色的;ide
给出 q 次询问,询问分为一、2两种:ui
- 将节点 x 涂成黑色。
- 询问节点 x 到全部的黑点节点的简单路径中的标号最小的那个点(包括起点和黑点)
题目保证第一次询问是 1 类型的。url
•题解
若是咱们随便选取某节点做为根节点,那么询问的时候,咱们要找到节点 x 到全部黑色节点的 LCA;spa
可是这样显然会超时的,因此咱们换一种建树方法。3d
因为第一个询问必然是 1 类型,那么咱们就把第一次询问的那个变黑的点做为根节点,看一下这样有什么好处;code
定义 $res_i$ 表示节点 i 到根节点(询问1的x)的路径中,标号最小的节点;blog
首先,咱们预处理出全部的 $res$,只需 $DFS$ 一遍便可,时间复杂度 $O(n)$;get
接下来,若是剩余的询问所有是 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 }