day45-李天晓
T1 CF512D Fox And Travelling
- 给定一张 n 个点 m 条边的无向图。
- 一个点只有当与它直接相连的点中最多只有一个点未被遍历过时才可被遍历。
- 询问对于每个 \(k \in [0,n]\),遍历 k 个点的方案数。
- \(n \le 100,m \le \frac{n(n-1)}{2}\) ,答案对 \(10^9+9\) 取模
solution
明确一点,环上的点是不可能被遍历到的。
所以,第一步就是处理出所有的能被遍历到的点。
然后,考虑去掉了环,那么剩下的这些树(剩下的一定是树)中树分两种,
一种是所有的点都能被遍历到,另一种是它的根与环上的某一点相连,那么根只能最后被遍历到,
于是设计一个dp , \(dp[x][i]\) 表示 x的子树中选择i个点方案数。
\(dp[x][j+k] = dp[x][j] * dp[v][k] * C(j + k , j)\)
对于第二种树,显然只能以与环上相连的点为根,第一种树可以以所有的点为根进行一次dp,之后再减去算重的,
令tot为总个数,那么选i个就会被算tot - i次。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 105 , mod = 1e9+9;
inline int read()
{
register int x = 0 , f = 0; register char c = getchar();
while(c < '0' || c > '9') f |= c == '-' , c = getchar();
while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48) , c = getchar();
return f ? -x : x;
}
int n , m , rot;
int du[N] , ail[N] , vis[N] , now[N] , siz[N] , C[N][N] , f[N][N] , inv[N];
vector<int> G[N] , tmp;
void topsort()
{
static queue<int> q;
for(int i = 1 ; i <= n ; ++i) if(du[i] <= 1) q.push(i) , ail[i] = 1;
while(q.size())
{
int x = q.front(); q.pop();
for(auto v : G[x]) if(!ail[v] && (--du[v]) <= 1) ail[v] = 1 , q.push(v);
}
}
void Getroot(int x , int fa)
{
vis[x] = 1; tmp.push_back(x);
for(auto v : G[x]) if(v != fa) if(!ail[v]) rot = x; else Getroot(v , x);
}
void calc(int u , int v)
{
static int tmp[N]; memset(tmp , 0 , sizeof tmp);
for(int i = 0 ; i <= n ; ++i)
for(int j = 0 ; i + j <= n ; ++j)
tmp[i+j] = (tmp[i+j] + (LL)f[u][i] * f[v][j] % mod * C[i+j][i] % mod) % mod;
for(int i = 0 ; i <= n ; ++i) f[u][i] = tmp[i];
}
void dfs(int x , int fa)
{
memset(f[x] , 0 , sizeof f[x]);
f[x][0] = siz[x] = 1;
for(auto v : G[x]) if(v != fa && ail[v]) dfs(v , x) , siz[x] += siz[v] , calc(x , v);
f[x][siz[x]] = f[x][siz[x] - 1];
}
int main()
{
n = read(); m = read();
for(int i = 1 , u , v; i <= m ; ++i)
du[u = read()]++ , du[v = read()]++ , G[u].push_back(v) , G[v].push_back(u);
topsort(); f[0][0] = 1;
for(int i = 0 ; i <= n ; ++i)
{
C[i][i] = C[i][0] = 1;
for(int j = 1 ; j < i ; ++j) C[i][j] = ((LL)C[i-1][j] + C[i-1][j-1]) % mod;
}
inv[0] = inv[1] = 1;
for(int i = 2 ; i <= n ; ++i) inv[i] = ((LL)mod - mod / i) * inv[mod % i] % mod;
for(int i = 1 ; i <= n ; ++i) if(!vis[i] && ail[i])
{
rot = -1; tmp.clear(); Getroot(i , 0);
if(~rot)
dfs(rot , 0) , calc(0 , rot);
else
{
int tot = tmp.size(); memset(now , 0 , sizeof now);
for(auto v : tmp)
{
dfs(v , 0);
for(int j = 0 ; j <= n ; ++j) now[j] = ((LL)now[j] + f[v][j]) % mod;
}
for(int j = 0 ; j <= tot ; ++j) now[j] = (LL)now[j] * inv[tot - j] % mod;
memset(f[tmp[0]] , 0 , sizeof f[tmp[0]]);
for(int j = 0 ; j <= tot ; ++j) f[tmp[0]][j] = now[j];
calc(0 , tmp[0]);
}
}
for(int i = 0 ; i <= n ; ++i) cout << f[0][i] << '\n';
return 0;
}