[cf1610I]Mashtali vs AtCoder
考虑$k=1$时的问题(即AGC017D),可以参考这里(与后面有关系,建议阅读)
而对于所有$k$,仍以1为根建树,并将整棵树分为若干个独立的问题——
1.对于内部不存在固定点的极大子树,显然其再加上根父亲即是一个独立的问题,结合上题结论,这个问题的sg值为这棵子树的sg值+1
2.对于去掉上述子树后剩下的部分,不妨看作一棵无根树,其满足度为1的节点均是固定点(其余点也可以是固定点,但事实上并不关心),且显然这也是一个独立的问题
结论:这棵树的sg值为边数$mod\ 2$
引理:对于存在固定点的树(不要求度为1的节点均是固定点),其sg值$\equiv $边数$(mod\ 2)$
将引理和结论一起对节点个数归纳,考虑此时的一棵树,对其分类讨论:
1.若该树所有度为1的节点均是固定点,考虑仅一次操作不会导致删除,因此去掉一条边后两个子问题的sg值异或和由引理$\not\equiv$边数$(mod\ 2)$,进而其$mex$可以取到边数$mod\ 2$,也即$sg\le $边数$mod\ 2$
当边数为偶数时结论已经成立,考虑边数为奇数时,任取一个度为1的节点和距其最近的度不为2的节点(对于度为2的节点,总有唯一的出边,因此该点唯一),并在两者的路径上去边
假设路径长度为$l$,将$l$分为$x$和$y$两段(其中$x+y=l-1$),这两段在新问题中都将作为第一种情况,而去掉后又是一个第2种情况,可以由归纳假设确定其sg值
具体的,不难得到这个新问题的sg值为$x\oplus y\oplus (l\ mod\ 2)\oplus 1$,再对$l$的奇偶性分类讨论即可构造$x$和$y$使得其的值为0,也即$sg$恰为1(边数为奇数时)
同时,显然对于引理也即成立
2.若该树所有度为1的节点不均是固定点,将其按照之前的方式分为若干个独立的问题,注意到每一条边恰属于一个子问题,且每一个子问题点数均严格小于该树,由归纳假设成立
结合sg函数的性质,将上述所有(独立的)问题sg值异或即为原问题的sg值
实现上,预处理出所有子树的sg值以及$k=1$时的答案,考虑将$k$固定并维护答案
考虑$k$向上所有子树内不存在(除$k$以外的)固定点的位置,假设依次为$a_{1},a_{2},...,a_{t}$(包括$k$本身),分析可得即将答案异或上$\bigoplus_{i=1}^{t}sg_{a_{i}}\oplus (sg_{a_{i}}+1)$,且第2部分的边数加上$t$
这个如何找直接暴力即可,注意到一个节点至多被暴力找一次,均摊总复杂度为$o(n)$
总复杂度为$o(n)$,可以通过
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 300005 4 vector<int>v[N]; 5 int n,x,y,cnt,ans,f[N],vis[N],sg[N]; 6 void dfs(int k,int fa){ 7 f[k]=fa; 8 for(int i=0;i<v[k].size();i++) 9 if (v[k][i]!=fa){ 10 dfs(v[k][i],k); 11 sg[k]^=sg[v[k][i]]+1; 12 } 13 } 14 int main(){ 15 scanf("%d",&n); 16 for(int i=1;i<n;i++){ 17 scanf("%d%d",&x,&y); 18 v[x].push_back(y); 19 v[y].push_back(x); 20 } 21 dfs(1,0); 22 cnt=0,ans=sg[1],vis[1]=1; 23 if (ans)printf("1"); 24 else printf("2"); 25 for(int i=2;i<=n;i++){ 26 for(int j=i;!vis[j];j=f[j]){ 27 vis[j]=1; 28 cnt++,ans^=(sg[j]^(sg[j]+1)); 29 } 30 if (ans^(cnt&1))printf("1"); 31 else printf("2"); 32 } 33 return 0; 34 }