标签:fine bool stdin bzoj 定义 链接 for efi zoj
题目链接:实验比较
如果我们把相等关系全部缩起来的话,这道题给出的小于关系如果有环,那么就是不合法的,否则就构成了一片森林。
定义等于号连起来的所有变量看做一个块。
然后我们就可以令\(f_{i,j}\)表示以\(i\)为根的子树中分成了\(j\)个块的方案数。如果我们在给森林添加一个虚根,把它变成一棵树的话,最终的答案就很好算了。
然后我们考虑一下如何转移。设\(v\)是\(u\)的子节点,那么\(f_{u,i}\)和\(f_{v,j}\)可以转移到\(f_{u,k}(\max(i,j)\le k \le i+j)\)。具体转移的式子为:
\[f_{u,k}=f_{u,i}f_{v,j}\binom{k}{i}\binom{i}{j-(k-i)}\]
可以看成\(i\)个块先占了\(i\)个地方,然后\(v\)子树的\(j\)个块先把空余的位置占满,再随便找位置合并。注意转移的时候不要把需要用的信息给覆盖了,需要用一个数组来辅助转移。
由于根节点不能和子树内任意一个节点相同,所以最后再把根节点加入到\(dp\)值中即可。注意虚根需要特判一下。
下面贴代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) #define mod 1000000007 #define maxn 100010 #define maxm 400010 using namespace std; typedef long long llg; int n,m,du[maxn]; int head[maxn],next[maxm],to[maxm],tt; llg f[maxn],ans; bool vis[maxn]; int getint(){ int w=0;bool q=0; char c=getchar(); while((c>‘9‘||c<‘0‘)&&c!=‘-‘) c=getchar(); if(c==‘-‘) c=getchar(),q=1; while(c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar(); return q?-w:w; } void gi(llg &x){if(x>=mod) x%=mod;} llg mi(llg a,int b){ llg s=1; while(b){ if(b&1) s=s*a,gi(s); a=a*a,gi(a); b>>=1; } return s; } llg dfs(int u){ if(vis[u]) return f[u]; vis[u]=1; for(int i=head[u];i;i=next[i]) f[u]+=dfs(to[i]),gi(f[u]); f[u]*=mi(du[u],mod-2); gi(f[u]); return f[u]; } int main(){ File("maple"); n=getint(); m=getint(); int x=getint(),y=getint(); while(m--){ int u=getint(),v=getint(); du[v]++; to[++tt]=v;next[tt]=head[u];head[u]=tt; } du[y]++; ans=1; for(int i=2;i<=n;i++) ans*=du[i],gi(ans); if(y!=1 && x!=1){ vis[x]=1; f[x]=ans*mi(du[x],mod-2); gi(f[x]); ans-=dfs(y); ans%=mod; if(ans<0) ans+=mod; } printf("%lld\n",ans); return 0; }
标签:fine bool stdin bzoj 定义 链接 for efi zoj
原文地址:http://www.cnblogs.com/lcf-2000/p/6431225.html