Legend
Link ( extrm{to LOJ})。
Editorial
这是一个 (O(n^2)) 的做法。
题意各位大佬都讲得很清楚了,现在可以转化成一个问题是:给定一个内向森林,祖先结点的颜色必须严格大于后代结点。颜色必须为连续正整数,求方案。
先考虑一棵树的答案:
设 (g(n)) 表示所有节点有 (n) 种颜色且满足题意的方案数。
然后我们可以设 (f(n)= sumlimits_{i=0}^{n}inom{n}{i}g(i))。
21.1.13upd:这样看着可能不太明确其含义,补一下:我们可以设 (f[i][j]) 表示在 (i) 子树内的所有节点,颜色小于 (j),(i) 的颜色恰好是 (j) 的方案数。即,不要求选出的颜色的值域是 (1sim k) 连续的。这样就可以树形 dp 了。
不难发现 (f(i) (1 le i le n)) 可以通过一个 (O(n^2)) 的前缀和优化树形 dp 求出。
根据二项式反演 (g(n)=sumlimits_{i=0}^{n} inom{n}{i}(-1)^{n-i}f(i)) 我们就可以得到答案了。
总复杂度仅为 (O(n^2))。
Code
而在本题中,是内向树森林,我们建立一个虚结点 (n+1),与所有树的根连边。
运行上述算法时,我们对虚结点的转移进行单独判断即可。
int ind[MX];
int head[MX] ,tot;
struct edge{
int node ,next;
}h[MX << 1];
void addedge(int u ,int v){
h[++tot] = (edge){v ,head[u]} ,head[u] = tot;
ind[v]++;
}
LL dp[MX][MX] ,S[MX][MX] ,C[MX][MX];
int fa[MX] ,n ,m;
void init(){
for(int i = 1 ; i < MX ; ++i) fa[i] = i;
for(int i = 0 ; i < MX ; ++i) C[i][0] = 1;
for(int i = 1 ; i < MX ; ++i)
for(int j = 1 ; j < MX ; ++j)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD;
}
int find(int x){return fa[x] == x ? x : fa[x] = find(fa[x]);}
void link(int u ,int v){u = find(u) ,v = find(v) ,fa[u] = v;}
int cnt ,ins[MX];
void dapai(int x){
if(ins[x]) puts("0") ,exit(0);
ins[x] = 1;
for(int i = 1 ; i <= cnt ; ++i) dp[x][i] = 1;
for(int i = head[x] ,d ; i ; i = h[i].next){
dapai(d = h[i].node);
for(int j = 1 ; j <= cnt ; ++j){
if(x == n + 1) dp[x][j] = dp[x][j] * S[d][j] % MOD;
else dp[x][j] = dp[x][j] * S[d][j - 1] % MOD;
}
}
ins[x] = 0;
for(int i = 1 ; i <= cnt ; ++i) S[x][i] = (S[x][i - 1] + dp[x][i]) % MOD;
}
int ecnt;
struct EDGE{int u ,v;}E[MX];
int main(){
init();
n = read() ,m = read();
for(int i = 1 ,u ,v ; i <= m ; ++i){
char type[233];
scanf("%d%s%d" ,&u ,type ,&v);
if(type[0] == '=') link(u ,v);
else E[++ecnt] = (EDGE){u ,v};
}
for(int i = 1 ; i <= ecnt ; ++i){
addedge(find(E[i].u) ,find(E[i].v));
}
int in = 0;
for(int i = 1 ; i <= n ; ++i){
if(find(i) == i) ++cnt;
if(find(i) == i && !ind[i]){
addedge(n + 1 ,find(i));
++in;
}
}
if(!in){
puts("0");
return 0;
}
dapai(n + 1);
LL ans = 0;
for(int j = 1 ; j <= cnt ; ++j){
LL tmp = 0;
for(int i = 0 ; i <= j ; ++i){
LL add = dp[n + 1][i] * ((j - i) & 1 ? -1 : 1) * C[j][i] % MOD;
add = (add + MOD) % MOD;
tmp = (tmp + add) % MOD;
}
debug("stained %d color %lld
" ,j ,tmp);
ans = (ans + tmp) % MOD;
}
printf("%lld
" ,ans);
return 0;
}