zoukankan      html  css  js  c++  java
  • 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
    ",i,f[0][i]);
    	for (int i=1;i<=siz[0];i++) ans=(ans+f[0][i])%mod;
    	printf("%lld
    ",ans);
    	return 0;
    }



  • 相关阅读:
    文件参数Python读取wav格式文件
    电子工程术语和定义列表,按字母顺序排列
    MAC地址加减1算法
    uboot通过kernel command line 动态分区 CONFIG_MTD_CMDLINE_PARTS
    c调用shell脚本
    busybox提示can't access tty.job control turned off
    cut命令如何截取以空格隔开的字段
    DS28E01100
    busybox 中的ntpd使用
    Debugfs
  • 原文地址:https://www.cnblogs.com/thythy/p/5493495.html
Copyright © 2011-2022 走看看