题解:P3427 [POI2005] DZI-Hollows
posted on 2024-11-24 23:38:08 | under | source
题意:有两棵树,每个树有无穷个不同的洞,需要将 \(n\) 个鸟分配到不同的洞里。同时给出 \(m\) 对关系,定义一种方案是合法的当且仅当将所有关系连线不交叉(可以同一个点),且连线必须在两棵不同的树间。认为两种方案不同当且仅当存在一只鸟在两种方案中位于不同的树上或同一棵树上相对位置不同。
题意真的恶心,不过应该能发现如下几点:
-
应当为二分图,所以不能存在奇环。其实偶环也不行因为不可能调整至不交叉。
-
连边构成若干连通块,除去孤立点,则连通块之间不能交叉,因此单独计算一种连通块的方案最后再乘起来。
手玩下感觉合法方案类似于一条链上长着一些菊花。对于任意一点,假如存在 \(p\) 个相邻节点还与其它点连边,那么它们向外连边的方向不能一样且只能放在两侧,否则一定交叉,那么 \(p>2\) 时无解。假如存在 \(q\) 个相邻节点只与它相连,那么它们可以随意交换顺序所以乘上个阶乘然后扔掉。
一番调整后所有点度数 \(\le 2\) 且连通,所以就是条链,假如节点数 \(\ge 2\) 那么可以将链左右翻转得到不同方案,也就是乘个 \(2\)。方案也可以上下翻转再乘个 \(2\)。
所有连通块的方案乘起来再乘个阶乘,现在加入 \(k\) 个孤立点,放在哪里都行,方案数为 \(A^{gl}_{n+1}\)。复杂度 \(O(n)\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define NO {puts("0"); return 0;}
const int N = 1e6 + 5;
int n, m, mod, u, v, jc[N], ans, gl, whl, col[N];
bool cir;
vector<int> to[N], fhr;
inline void dfs(int u, int from, int c){
col[u] = c, fhr.push_back(u);
for(auto v : to[u])
if(!col[v]) dfs(v, u, 3 - c);
else if(v ^ from) cir = true;
}
signed main(){
cin >> n >> m >> mod;
ans = jc[0] = 1;
for(int i = 1; i < N; ++i) jc[i] = 1ll * jc[i - 1] * i % mod;
for(int i = 1; i <= m; ++i) scanf("%d%d", &u, &v), to[u].push_back(v), to[v].push_back(u);
for(int i = 1; i <= n; ++i)
if(!col[i]){
if(to[i].empty()) {++gl; continue;}
cir = false, fhr.clear();
dfs(i, 0, 1);
if(cir) NO
for(auto p : fhr){
int cnt = 0;
for(auto p2 : to[p]) if(to[p2].size() == 1) ++cnt, col[p2] = 3;
ans = 1ll * ans * jc[cnt] % mod;
}
int cnt = 0;
for(auto p : fhr){
int du = 0;
for(auto p2 : to[p])
if(col[p] != 3 && col[p2] != 3) ++du;
if(du > 2) NO
cnt += col[p] != 3;
}
if(cnt >= 2) ans = 2ll * ans % mod;
ans = 2ll * ans % mod;
++whl;
}
for(int i = n + 2 - gl; i <= n + 1; ++i) ans = 1ll * ans * i % mod;
cout << 1ll * ans * jc[whl] % mod;
return 0;
}

浙公网安备 33010602011771号