【杂题】10.7爬树

是该来道小清新思考题醒醒脑的时候了

题目大意

一颗$n$个点初全为白色的树,有$q$个操作:

  1. 将指定点$x$变为黑色
  2. 查询点$x$到所有黑色点路径上,编号最小的点

$n,q \le 10^6$


题目分析

$O(n\log n)$的做法略过不谈

首先把第一个黑点$rt$作为根节点,然后预处理一个$f_i$表示$i$点到$rt$路径上最小的节点作为初始答案。

接下去考虑每一个黑点$i$会对其他点$j$造成什么影响。

【杂题】10.7爬树_第1张图片

可以分为如上三种情况讨论。

$\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
 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) 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

你可能感兴趣的:(【杂题】10.7爬树)