[arc079f]namori grundy
题意:
给出一个基环外向树,每个点有一个$sg$值为所有子节点的$mex$值,求是否有设置$sg$值的方案满足条件。
题解:
感觉没有B题难。。。听完讲评就会了。。。
这题栋爷有一种拆环的做法,但是我写的是讲题的时候ckw讲的一种神仙做法,个人感觉容易理解一点qwq
如果觉得ckw的博客过于神仙可以看看hy写的题解,讲的很清楚qwq(我太垃圾了)
对了此题还有lzx的一种神仙做法qwq(我太太太垃圾了)
首先如果是一棵树,显然必定有解,所有叶节点设0,然后取mex即可;
考虑拓展到基环外向树上,这个树的结构是一个环上的节点和各自的子树构成,那么不妨先对每个子树求出各自的$sg$值(必定有解),然后把环扒出来单独看;
此时环上的每个节点都有了一个初始的$sg$值,不难发现这个值就是解的下界,现在要做的就是在下界上构造出满足题目要求的解;
考虑两个在环上相邻的节点$u\rightarrow v$,设它们的$sg$值分别为$sg_u$和$sg_v$,分三类考虑:
1.若$sg_u>sg_v$,那么不用做任何改动,因为$sg_v$已经被包含在$u$的子节点$sg$中了;
2.若$sg_u=sg_v$,那么把$sg_u$变成$sg_u+1$即可;
3.若$sg_u<sg_v$,也不需要任何改动。。。因为$sg_v$并不在$sg_u$的范围内;
所以可以发现两个点只要相等就可以通过+1来调整,只要不等就不需要再调整,所以肯定会有解;
那么无解情况是什么呢?考虑所有初始值都相等的情况,此时我们需要对隔一个点加一来使得每对相邻点都不相等,环长度为偶数时正好加完,但是当环的长度为奇数的时候加完一圈回去奇偶性就改变了,所以要一直无限的加下去,明显这就是无解的情况。
暴力dfs模拟上述过程即可
代码:
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 using namespace std;
7 struct edge{
8 int v,next;
9 }a[200001];
10 int n,now,maxn=-2147483647,minn=2147483647,bcc=0,tot=0,sg[200001],head[200001],p[200001];
11 bool used[200001],huan[200001];
12 void add(int u,int v){
13 a[++tot].v=v;
14 a[tot].next=head[u];
15 head[u]=tot;
16 }
17 void dfs(int u){
18 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
19 int v=a[tmp].v;
20 if(!huan[v])dfs(v);
21 }
22 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
23 int v=a[tmp].v;
24 if(!huan[v])used[sg[v]]=true;
25 }
26 for(;used[sg[u]];sg[u]++);
27 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
28 int v=a[tmp].v;
29 if(!huan[v])used[sg[v]]=false;
30 }
31 }
32 int main(){
33 memset(head,-1,sizeof(head));
34 memset(sg,0,sizeof(sg));
35 scanf("%d",&n);
36 for(int i=1;i<=n;i++){
37 scanf("%d",&p[i]);
38 add(p[i],i);
39 }
40 now=1;
41 for(;!used[now];now=p[now])used[now]=1;
42 for(;!huan[now];now=p[now])huan[now]=1;
43 memset(used,0,sizeof(used));
44 for(int i=1;i<=n;i++){
45 if(huan[i]){
46 dfs(i);
47 bcc++;
48 maxn=max(maxn,sg[i]);
49 minn=min(minn,sg[i]);
50 }
51 }
52 if(maxn==minn&&bcc%2)printf("IMPOSSIBLE");
53 else printf("POSSIBLE");
54 return 0;
55 }