题解:CF2104G Modulo 3
题目分析
首先我们发现,一个环上的点一定会被染成一种颜色。 所以我们考虑先缩一下点。
那么现在一定是一个 DAG。然后我们发现只要按照拓扑序染色,那么每个点想是什么颜色就能是什么颜色。
设 DAG 上的点个数为
由于原图每个点只有一条出边,所以强连通分量只能是一个环,且每个点最多在一个环里出现。
问题转化成统计图上环的数量。
还是由于每个点只有一条出边,我们可以把有向边看成无向边。
那么维护连通块修改不是很方便,所以我们先用线段树分治变成只加边。
但是在加入一条边
在答案统计中,点数是放在指数上的,根据欧拉定理,它应该是模 2。
所以距离只有 0 和 1 两种,在此基础下异或跟加减法的结果一样,所以直接用带权并查集即可。
时间复杂度
顺带一提,可以 LCT 维护基环树,做到
Code
就放 LCT 维护基环树了。
#include<bits/stdc++.h>
using namespace std;
//
const int maxn=2e5+10;
const int mod=3;
int n,q;
int to[maxn];
int vis[maxn];
int res;
inline int Pow(int x,int y){
x%=mod;
int s=1;
for(;y;x=x*x%mod,y>>=1)if(y&1)s=s*x%mod;
return s;
}
struct node{
int fa,son[2];
int sz,val;
int tagr,tagm;
node(){fa=son[0]=son[1]=sz=val=tagr=0,tagm=-1;}
}tree[maxn];
inline void init(){for(int i=1;i<=n;i++)tree[i].sz=1;}
inline int isroot(int v){return v!=tree[tree[v].fa].son[0]&&v!=tree[tree[v].fa].son[1];}
inline int get(int v){return v==tree[tree[v].fa].son[1];}
inline void connect(int f,int v,int type){tree[f].son[type]=v;if(v)tree[v].fa=f;}
inline void pushup(int v){
tree[v].sz=tree[tree[v].son[0]].sz+tree[tree[v].son[1]].sz+1;
}
inline void reverse(int v){
swap(tree[v].son[0],tree[v].son[1]);
tree[v].tagr^=1;
}
inline void modify(int v,int k){
tree[v].val=k;
tree[v].tagm=k;
}
inline void pushdown(int v){
if(tree[v].tagr){
reverse(tree[v].son[0]);
reverse(tree[v].son[1]);
tree[v].tagr=0;
}
if(tree[v].tagm!=-1){
modify(tree[v].son[0],tree[v].tagm);
modify(tree[v].son[1],tree[v].tagm);
tree[v].tagm=-1;
}
}
inline void pushtag(int v){if(!isroot(v))pushtag(tree[v].fa);pushdown(v);}
inline void rotate(int v){
int f=tree[v].fa,g=tree[f].fa,chk=get(v);
if(!isroot(f))tree[g].son[get(f)]=v;
tree[v].fa=g;
connect(f,tree[v].son[chk^1],chk);
connect(v,f,chk^1);
pushup(f);
pushup(v);
}
inline void splay(int v){
pushtag(v);
while(!isroot(v)){
int f=tree[v].fa;
if(!isroot(f))rotate(get(f)==get(v)?f:v);
rotate(v);
}
}
inline void access(int v){
int p=0;
while(v){
splay(v);
tree[v].son[1]=p;
pushup(v);
p=v,v=tree[v].fa;
}
}
inline void mkroot(int v){
access(v);
splay(v);
reverse(v);
}
inline int findroot(int v){
access(v);
splay(v);
while(tree[v].son[0])v=tree[v].son[0];
splay(v);
return v;
}
inline void split(int x,int y){
mkroot(x);
access(y);
splay(y);
}
inline void link(int x,int y){
mkroot(x),mkroot(y);
tree[x].fa=y;
}
inline void cut(int x,int y){
split(x,y);
tree[y].son[0]=tree[x].fa=0;
pushup(y);
}
inline int ask(int x){
access(x),splay(x);
return tree[x].val;
}
inline void insert(int p){
int x=p,y=to[p];
if(findroot(x)!=findroot(y))link(x,y);
else{
split(x,y);
res-=tree[y].sz-1;
modify(y,p);
vis[p]=1;
}
}
inline void delet(int p){
int x=p,y=to[p];
if(vis[p]){
split(x,y);
res+=tree[y].sz-1;
modify(y,0);
vis[p]=0;
}else if(ask(y)==ask(x)&&ask(x)){
p=ask(x);
split(p,to[p]);
res+=tree[to[p]].sz-1;
modify(to[p],0);
vis[p]=0;
cut(x,y);
link(p,to[p]);
}else cut(x,y);
}
int main(){
cin>>n>>q;
init();
res=n;
for(int i=1;i<=n;i++){
cin>>to[i];
insert(i);
}
while(q--){
int x,y,k;
cin>>x>>y>>k;
delet(x);
to[x]=y;
insert(x);
cout<<Pow(k,res)<<'\n';
}
return 0;
}