bzoj4013: [HNOI2015]实验比较
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4013
思路:首先把等于的缩成一个点,由好的向坏的连边,有环肯定无解。
然后题目里说“小 D都最多只记住了某一张质量不比 i 差的另一张图片 Ki”
那就是每个点就最多只有一条入边,那存在合法方案的图就一定是森林。
加一个虚根,这可以树形DP了。
假设f[i][j]表示i号点的子树中的所有点构成的有且只有j个小于号的序列的个数
那么考虑怎么合并两个子树的答案
假设现在的两个儿子是x,y,子树大小分别为siz[x],siz[y]
枚举两个儿子的序列小于号个数i,j。
那么合并出来的新序列小于号个数k就在max(i,j)到i+j之间。
那么问题就是求对于k个盒子,有i个白球,j个黑球,求有多少种方案。
答案就是f[x][i]*f[y][j]*C[k][i]*C[i][j-(k-i)] C是组合数。
实现的时候,再开一个g数组,g[i]就是之前的儿子的子树的点构成的小于号为j个的序列方案数
注意儿子合并完后,因为新加了当前点,序列长度要+1
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> const int maxn=105,maxm=205,mod=(int)(1e9+7); typedef long long ll; using namespace std; int n,m,pre[maxm],now[maxn],son[maxm],tot,fa[maxn],cnt,deg[maxn],siz[maxn];char op[3]; ll c[maxn][maxn],f[maxn][maxn],ans,g[maxn]; bool bo[maxn]; struct node{int x,y;}li[maxn]; void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b,deg[b]++;} int getfa(int x){return x==fa[x]?x:fa[x]=getfa(fa[x]);} bool dfs(int x,int fa){ bo[x]=1;bool fir=1; for (int y=now[x];y;y=pre[y]) if (son[y]!=fa){ if (bo[son[y]]) return 0; if (!dfs(son[y],x)) return 0; if (!fir){ memset(g,0,sizeof(g)); for (int i=1;i<=siz[x];i++) if (f[x][i]) for (int j=1;j<=siz[son[y]];j++) if (f[son[y]][j]) for (int k=max(i,j);k<=i+j;k++) g[k]=(g[k]+f[x][i]*f[son[y]][j]%mod*c[k][i]%mod*c[i][j-(k-i)]%mod)%mod; siz[x]+=siz[son[y]]; for (int i=1;i<=siz[x];i++) f[x][i]=g[i]; } else{ fir=0,siz[x]=siz[son[y]]; for (int i=1;i<=siz[son[y]];i++) f[x][i]=f[son[y]][i]; } } if (x){ siz[x]++;if (fir) f[x][1]=1; else for (int i=siz[x];i>=1;i--) f[x][i]=f[x][i-1]; } return 1; } int main(){ scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) fa[i]=i; for (int i=0;i<=n;i++) c[i][0]=1; for (int i=1;i<=n;i++) for (int j=1;j<=i;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod; for (int i=1,x,y;i<=m;i++){ scanf("%d%s%d",&x,op,&y); if (op[0]=='='){fa[getfa(x)]=getfa(y);continue;} if (op[0]=='>') swap(x,y); li[++cnt]=(node){x,y}; } for (int i=1;i<=cnt;i++){ int x=li[i].x,y=li[i].y; if (getfa(x)!=getfa(y)) add(getfa(x),getfa(y)); else return puts("0"),0; } for (int i=1;i<=n;i++) if (!deg[getfa(i)]) add(0,getfa(i)); if (!dfs(0,-1)) return puts("0"),0; //for (int i=1;i<=n;i++) printf("%d %lld\n",i,f[0][i]); for (int i=1;i<=siz[0];i++) ans=(ans+f[0][i])%mod; printf("%lld\n",ans); return 0; }