BZOJ 4013: [HNOI2015]实验比较【树形DP】

题目传送门
分析:
直接贴dalao博客。。
其实还是比较好想,只不过判环的时候。。。
于是后来终于想起来判环的标准方式。。。

  • 一个vis数组标记有没有访问,一个inq数组标记是否在这次的搜索栈中,如果访问到在栈中的点就说明有环,退出dfs时出栈
bool check(int u){
	vis[u]=inq[u]=1;
	for(int i=fir[u];i;i=nxt[i]) if(inq[to[i]]||!check(to[i])) return 0;
	inq[u]=0;
	return 1;
}
for(int i=1;i<=cnt;i++) if(!vis[i]&&!check(i)) return puts("0"),0;

另外还有 n 3 n^3 n3的复杂度是怎么来的

  • 由于算 f [ u ] [ i ] f[u][i] f[u][i]的时候枚举 j ∈ [ 1 , s i z [ u ′ ] ] ,   k ∈ [ 1 , s i z [ v ] ] j\in[1,siz[u&#x27;]],~k\in[1,siz[v]] j[1,siz[u]], k[1,siz[v]]实际上就相当于在枚举两个子树中的点两两配对,两个点只有在它们的lca处会被枚举到,点对的数量是 n 2 n^2 n2级,所以是 n 2 n^2 n2的复杂度,外面再套一层循环i,就接近 n 3 n^3 n3

code:

#include<cstdio>
#include<cctype>
#include<algorithm>
#define maxn 105
using namespace std;
const int mod = 1e9+7;
inline void read(int &a){
	char c;while(!isdigit(c=getchar()));
	for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
}
int n,m,cnt,siz[maxn],c[maxn][maxn],scc[maxn],in[maxn];
int fir[maxn],nxt[maxn],to[maxn],tot;
bool vis[maxn],inq[maxn];
inline void line(int x,int y){nxt[++tot]=fir[x];fir[x]=tot;to[tot]=y;}
int F[maxn],x[maxn],y[maxn],f[maxn][maxn],g[maxn];
char op[maxn];
int find(int x){return x==F[x]?x:F[x]=find(F[x]);}
void dfs(int u)
{
	siz[u]=1,f[u][1]=1;
	for(int t=fir[u],v;t;t=nxt[t]){
		dfs(v=to[t]);
		for(int i=siz[u]+siz[v];i>=1;i--) g[i]=0;
		for(int i=siz[u]+siz[v];i>=1;i--)
			for(int j=min(siz[u],i);j>=1;j--)
				for(int k=min(i-1,siz[v]),ed=i-j;k>=ed;k--)
					g[i]=(g[i]+1ll*f[u][j]*f[v][k]%mod*c[i-1][j-1]%mod*c[j-1][k-i+j])%mod;
		siz[u]+=siz[v];
		for(int i=siz[u];i>=1;i--) f[u][i]=g[i];
	}
}
bool check(int u){
	vis[u]=inq[u]=1;
	for(int i=fir[u];i;i=nxt[i]) if(inq[to[i]]||!check(to[i])) return 0;
	inq[u]=0;
	return 1;
}
int main()
{
	read(n),read(m);
	c[0][0]=1;
	for(int i=1;i<=n;i++){
		c[i][0]=c[i][i]=1;
		for(int j=1;j<i;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
	}
	for(int i=1;i<=n;i++) F[i]=i;
	for(int i=1,u,v;i<=m;i++){
		read(x[i]);op[i]=getchar();read(y[i]);
		if(op[i]=='='){
			u=find(x[i]),v=find(y[i]);
			if(u<v) F[v]=u; else F[u]=v;
		}
	}
	for(int i=1;i<=n;i++)
		if(find(i)==i) scc[i]=++cnt;
		else scc[i]=scc[F[i]];
	for(int i=1;i<=m;i++) if(op[i]=='<') line(scc[x[i]],scc[y[i]]),in[scc[y[i]]]++;
	for(int i=1;i<=cnt;i++) if(!in[i]) line(cnt+1,i);
	for(int i=1;i<=cnt;i++) if(!vis[i]&&!check(i)) return puts("0"),0;
	dfs(cnt+1);
	int ans=0;for(int i=1;i<=cnt+1;i++) ans=(ans+f[cnt+1][i])%mod;
	printf("%d",ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值