【杂题】10.7爬树
是该来道小清新思考题醒醒脑的时候了
题目大意
一颗$n$个点初全为白色的树,有$q$个操作:
- 将指定点$x$变为黑色
- 查询点$x$到所有黑色点路径上,编号最小的点
$n,q \le 10^6$
题目分析
$O(n\log n)$的做法略过不谈
首先把第一个黑点$rt$作为根节点,然后预处理一个$f_i$表示$i$点到$rt$路径上最小的节点作为初始答案。
接下去考虑每一个黑点$i$会对其他点$j$造成什么影响。
可以分为如上三种情况讨论。
$\texttt{j}$在$\texttt{i}$的子树内:$i$只会贡献$i\cdots j$的路径,而这一部分已经被包括在$f_j$内了。
$\texttt{j}$在$\texttt{i}$到$\texttt{rt}$的路径上:虽然只有$i \cdots j$路径被贡献,但由于$j\cdots rt$的路径本身也被计入$f_i$,因此可以视作贡献了$f_j$.
$\texttt{i}$到$\texttt{j}$的路径跨越$\texttt{rt}$:$i \cdots j$的路径可以拆成$i\cdots rt$和$j\cdots rt$,也就是说$j$对$i$有$f_j$的贡献。
综上讨论可知,只要加进来一个黑点$i$,它就会对其他所有点产生$f_i$的贡献!
这比$O(n\log n)$的思路清新得多。
也算是一个警醒,想题目还是要逐层冷静分析,不要太浮躁。
1 #include<bits/stdc++.h> 2 const int maxn = 1000035; 3 const int maxm = 2000035; 4 5 int n,q,rt,ans,cnt,f[maxn]; 6 int edgeTot,head[maxn],nxt[maxm],edges[maxm]; 7 8 int read() 9 { 10 char ch = getchar(); 11 int num = 0, fl = 1; 12 for (; !isdigit(ch); ch=getchar()) 13 if (ch=='-') fl = -1; 14 for (; isdigit(ch); ch=getchar()) 15 num = (num<<1)+(num<<3)+ch-48; 16 return num*fl; 17 } 18 void addedge(int u, int v) 19 { 20 edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot; 21 edges[++edgeTot] = u, nxt[edgeTot] = head[v], head[v] = edgeTot; 22 } 23 void dfs(int x, int fa, int c) 24 { 25 f[x] = c; 26 for (int i=head[x]; i!=-1; i=nxt[i]) 27 if (edges[i]!=fa) dfs(edges[i], x, std::min(c, edges[i])); 28 } 29 void write(int x){if (x/10) write(x/10);putchar(x%10+'0');} 30 int main() 31 { 32 memset(f, 0x3f3f3f3f, sizeof f); 33 memset(head, -1, sizeof head); 34 n = read(), q = read()-1, cnt = 0x3f3f3f3f; 35 for (int i=1; i<n; i++) addedge(read(), read()); 36 rt = read(), rt = read()+1, dfs(rt, 0, rt); 37 for (int opt,pos; q; --q) 38 { 39 opt = read(), pos = (read()+ans)%n+1; 40 if (opt==1) cnt = std::min(cnt, f[pos]); 41 else ans = std::min(f[pos], cnt), write(ans), putchar('\n'); 42 } 43 return 0; 44 }
END