CF859E Desk Disorder

洛谷传送门 CF传送门

今天ChPu437大神讲了这个题,我也来写篇题解加深理解。

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;
}
posted @ 2020-10-29 15:24  jasony_sam  阅读(56)  评论(0编辑  收藏  举报