CF859E Desk Disorder
Solution
看题,什么又是方案数?我反手就是一个计数DP
我们转化题意,可以将一个人旧的座位和新的座位假装建一条边
那么这就是一张图,并且可能有多个连通块
因为每个连通块之间是互不影响的,所以我们可以分开考虑然后用乘法原理统计
so,这张图上会出现什么连通块呢
发现每个点的出度只能为 \(0\) 或 \(1\) ,分别对应着题中没人的座位和有人的座位
那连通块只有四种:1.树(即有一个出度为 \(0\) 的点)2.环(无出度为 \(0\) 的点)3.基环树(和2差不多)4.自环
开始考虑这几种情况
1.树
我们就以出度为 \(0\) 的点为根
考虑对于树上的每一个点,如果它想到它父亲的位置,它的父亲就也得往前,那么它到根这条路径上的点都得往前挪
什么?根怎么办?它又没有父亲,被占就被占了呗(。^▽^)
每个点都可以这样,所以方案数为树的 \(siz\) (注意,此时根的方案就是每个点都不动)
2.环和基环树
对于一个环,要么都往前,要么不动,方案数是 \(2\)
对于基环树的环上的每一个点,入度都为 \(2\) ,所以基环树的树上的点一个都不能动,不然环上的点会被两次占用
那么我们就可以将环和基环树一起考虑
所以基环树也是 \(2\)
3.自环
不能动,就是 \(1\)
情况讨论完了,怎么判环呢
我刚刚说的是假建图,所以并不是用dfs
那么什么东西可以查询两点之间的关系并且修改+记录呢——并查集登场了
这个题的做法就讲完捏♪(∇*)
Code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+10,mod=1e9+7;
int f[N],siz[N],n;
ll ans=1;
bool sf_cir[N],cir[N];
inline int find(int x){
return f[x]==x?x:f[x]=find(f[x]);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=2*n;i++){
f[i]=i;
siz[i]=1;
}
for(int i=1,x,y;i<=n;i++){
scanf("%d%d",&x,&y);
if(x==y) sf_cir[x]=1;
int fx=find(x),fy=find(y);
if(fx!=fy){
f[fx]=fy;
sf_cir[fy]|=sf_cir[fx];
siz[fy]+=siz[fx];
siz[fx]=0;
}
else cir[fx]=1;
}
for(int i=1;i<=2*n;i++){
if(find(i)==i&&!sf_cir[i]){
if(cir[i]) ans=ans*2%mod;
else ans=ans*siz[i]%mod;
}
}
printf("%lld\n",ans);
return 0;
}