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;
}



posted @ 2015-12-22 14:20  orzpps  阅读(124)  评论(0编辑  收藏  举报