[LOJ]#2117. 「HNOI2015」实验比较 DP

题解:

这题不会做……
一开始以为是拓扑序计数,觉得不太可做,后来仔细看题目才发现只有树的情况(有环则无解),然而还是不会。
难点是如何将两个状态合并。如果只记录方案数的话,是无法将两个状态合并的。所以设计状态时加多一维。 f i , j f_{i,j} fi,j表示以 i i i为根的子树,分为 j j j段的方案数,这里的每一段都是用等于号相连,而段与段之间用小于号相连,这样就可以转移了。具体一点的话就是枚举合并后有多少段,然后再乘个组合数。
注意图有可能是森林,为了方便,可以加一个虚根连向所有的根。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=110;
const int inf=2147483647;
const int mod=1000000007;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
void upd(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int n,m,f[Maxn][Maxn],g[Maxn],rt[Maxn],fa[Maxn],ex[Maxn],ey[Maxn],from[Maxn],deg[Maxn],sz[Maxn];
int findrt(int x){return((rt[x]==x)?x:rt[x]=findrt(rt[x]));}
struct Edge{int y,next;}e[Maxn<<1];
int last[Maxn],len=0;
void ins(int x,int y)
{
	int t=++len;
	e[t].y=y;e[t].next=last[x];last[x]=t;
}
int p;bool flag;
void go(int x)
{
	if(!x)return;
	if(x==p){flag=true;return;}
	go(from[findrt(x)]);
}
int C[Maxn][Maxn];
void pre()
{
	C[0][0]=1;
	for(int i=1;i<=n+1;i++)
	{
		C[i][0]=1;
		for(int j=1;j<=i;j++)
		C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
	}
}
void dfs(int x)
{
	sz[x]=0;
	if(!last[x]){sz[x]=f[x][1]=1;return;}
	bool fir=true;
	for(int i=last[x];i;i=e[i].next)
	{
		int y=e[i].y;
		dfs(y);
		if(fir)
		{
			fir=false;
			for(int j=1;j<=sz[y];j++)
			f[x][j]=f[y][j];
		}
		else
		{
			memset(g,0,sizeof(g));
			for(int j=1;j<=sz[x];j++)
			if(f[x][j])
			{
				for(int k=1;k<=sz[y];k++)
				if(f[y][k])
				{
					for(int l=max(j,k);l<=j+k;l++)
					upd(g[l],(LL)f[x][j]*f[y][k]%mod*C[l][j]%mod*C[j][k-(l-j)]%mod);
				}
			}
			for(int j=1;j<=sz[x]+sz[y];j++)f[x][j]=g[j];
		}
		sz[x]+=sz[y];
	}
	sz[x]++;
	for(int i=sz[x];i;i--)f[x][i]=f[x][i-1];
}
int main()
{
	memset(f,0,sizeof(f));
	n=read(),m=read();pre();
	for(int i=1;i<=n;i++)rt[i]=i;
	for(int i=1;i<=m;i++)
	{
		int x,y;char str[3];
		x=read();scanf("%s",str);y=read();
		if(str[0]=='=')
		{
			int fx=findrt(x),fy=findrt(y);
			if(fx==fy)continue;
			rt[fx]=fy;
		}
		else ex[i]=x,ey[i]=y,from[y]=x;
	}
	for(int i=1;i<=n;i++)
	{
		flag=false;p=i;
		go(from[findrt(i)]);
		if(flag)return puts("0"),0;
	}
	for(int i=1;i<=m;i++)
	if(ex[i])ins(findrt(ex[i]),findrt(ey[i])),deg[findrt(ey[i])]++;
	for(int i=1;i<=n;i++)if(findrt(i)==i&&!deg[i])ins(n+1,i);
	dfs(n+1);
	int ans=0;
	for(int i=1;i<=n+1;i++)upd(ans,f[n+1][i]);
	printf("%d",ans);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值