zoukankan      html  css  js  c++  java
  • [HNOI2015]实验比较

    Description
    (D)被邀请到实验室,做一个跟图片质量评价相关的主观实验。实验用到的图片集一共有(N)张图片,编号为(1)(N)。实验分若干轮进行,在每轮实验中,小(D)会被要求观看某两张随机选取的图片, 然后小(D)需要根据他自己主观上的判断确定这两张图片谁好谁坏,或者这两张图片质量差不多。 用符号"<",">"和"="表示图片(x)(y)(x)(y)为图片编号)之间的比较:

    如果上下文中(x)(y)是图片编号,则(x<y)表示图片(x)"质量优于"(y)(x>y)表示图片(x)"质量差于"(y)(x=y)表示图片(x)(y)"质量相同";

    也就是说,这种上下文中,"<"、">"、"="分别是质量优于、质量差于、质量相同的意思;在其他上下文中,这三个符号分别是小于、大于、等于的含义。图片质量比较的推理规则(在(x)(y)是图片编号的上下文中):

    (1)(x<y)等价于(y>x)

    (2)若(x<y)(y=z),则(x<z)

    (3)若(x<y)(x=z),则(z<y)

    (4)(x=y)等价于(y=x)

    (5)若(x=y)(y=z),则(x=z)

    实验中,小(D)需要对一些图片对((x, y)),给出(x<y)(x=y)(x>y)的主观判断。小(D)在做完实验后, 忽然对这个基于局部比较的实验的一些全局性质产生了兴趣。在主观实验数据给定的情形下,定义这(N)张图片的一个合法质量序列为形如"(x_1\,R_1\,x_2\,R_2\,x_3\,R_3\,…x_{N-1}\,R_{N-1}\,x_N)"的串,也可看作是集合({x_i\,R_i\,x_{i+1}|1leqslant ileqslant N-1}),其中(x_i)为图片编号,(x_1,x_2,…,x_N)两两互不相同(即不存在重复编号),(R_i)为<或=,"合法"是指这个图片质量序列与任何一对主观实验给出的判断不冲突。

    例如: 质量序列(3<1=2)与主观判断"(3>1,3=2)"冲突(因为质量序列中(3<1)(1=2),从而(3<2),这与主观判断中的(3=2)冲突;同时质量序列中的(3<1)与主观判断中的(3>1)冲突) ,但与主观判断"(2=1,3<2)"不冲突;因此给定主观判断"(3>1,3=2)"时,(1<3=2)(1<2=3)都是合法的质量序列,(3<1=2)(1<2<3)都是非法的质量序列。由于实验已经做完一段时间了,小(D)已经忘了一部分主观实验的数据。对每张图片(i),小(D)都最多只记住了某一张质量不比(i)差的另一张图片(K_i)。这些小(D)仍然记得的质量判断一共有(M)((0leqslant Mleqslant N)),其中第(i)条涉及的图片对为((K_{X_i},X_i)),判断要么是(K_{X_i}<X_i),要么是(K_{X_i}=X_i),而且所有的(X_i)互不相同。小(D)打算就以这(M)条自己还记得的质量判断作为他的所有主观数据。

    现在,基于这些主观数据,我们希望你帮小(D)求出这(N)张图片一共有多少个不同的合法质量序列。

    我们规定:如果质量序列中出现"(x=y)",那么序列中交换(x)(y)的位置后仍是同一个序列。因此:(1<2=3=4<5)(1<4=2=3<5)是同一个序列,(1<2=3)(1<3=2)是同一个序列,而(1<2<3)(1<2=3)是不同的序列,(1<2<3)(2<1<3)是不同的序列。由于合法的图片质量序列可能很多, 所以你需要输出答案对(10^9+7)取模的结果

    Input
    第一行两个正整数(N,M),分别代表图片总数和小(D)仍然记得的判断的条数;
    接下来(M)行,每行一条判断,每条判断形如"(x<y)"或者"(x=y)"​。

    Output
    输出仅一行,包含一个正整数,表示合法质量序列的数目对 10^9+7取模的结果。

    Sample Input
    5 4
    1 < 2
    1 < 3
    2 < 4
    1 = 5

    Sample Output
    5

    HINT
    不同的合法序列共5个,如下所示:
    1 = 5 < 2 < 3 < 4
    1 = 5 < 2 < 4 < 3
    1 = 5 < 2 < 3 = 4
    1 = 5 < 3 < 2 < 4
    1 = 5 < 2 = 3 < 4
    100%的数据满足N<=100。


    首先注意到题目中很重要的一句话,对每张图片(i),小(D)都最多只记住了某一张质量不比(i)差的另一张图片(K_i),于是我们将所有等于关系的点缩到一起,对于点(i),如果(K_i)存在,则(i)(K_i)连边,那么显然构成了森林,我们用超级root把森林变成树

    然后tree dp,设(f[u])表示(u)的子树内的方案数

    但是对于(u)的两个子树(v,w),可能存在一些点质量相同,因此我们需要再添加一维,设(f[u][i])表示(u)的子树内分成(i)端的方案数(每段里面的节点质量相等),设(f')表示更新之前的(f)值,转移有

    [f[u][i]=sumlimits_{j,k}f'[u][j] imes f[v][k] imes T ]

    (T)表示(j)段和(k)段合并成(i)段的方案数

    (f[u])的质量序列为(A)(f'[u])的质量序列为(B)(f[v])的质量序列为(C),把他们合并起来相当于枚举(B)中的(j-1)段在(A)中的位置(起始端点已经被(u)霸占),那么方案为(inom{i-1}{j-1}),然后把(C)中的(i-j)段放置到(A)中使得(A)非空,剩下的与(B)合并,方案为(inom{j-1}{k-i+j}),则(T=inom{i-1}{j-1} imesinom{j-1}{k-i+j})

    答案即为(Ans=sum f[root][i]),由于每对点都只在lca处被计算贡献了(O(n))次,因此复杂度为(O(n^3))

    /*program from Wolfycz*/
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define inf 0x7f7f7f7f
    using namespace std;
    typedef long long ll;
    typedef unsigned int ui;
    typedef unsigned long long ull;
    inline char gc(){
    	static char buf[1000000],*p1=buf,*p2=buf;
    	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
    }
    inline int frd(){
    	int x=0,f=1; char ch=gc();
    	for (;ch<'0'||ch>'9';ch=gc())	if (ch=='-')	f=-1;
    	for (;ch>='0'&&ch<='9';ch=gc())	x=(x<<3)+(x<<1)+ch-'0';
    	return x*f;
    }
    inline int read(){
    	int x=0,f=1; char ch=getchar();
    	for (;ch<'0'||ch>'9';ch=getchar())	if (ch=='-')	f=-1;
    	for (;ch>='0'&&ch<='9';ch=getchar())	x=(x<<3)+(x<<1)+ch-'0';
    	return x*f;
    }
    inline void print(int x){
    	if (x<0)	putchar('-'),x=-x;
    	if (x>9)	print(x/10);
    	putchar(x%10+'0');
    }
    const int N=1e2,Mod=1e9+7;
    int pre[(N<<1)+10],now[N+10],child[(N<<1)+10],tot;
    int fa[N+10],X[N+10],Y[N+10],top[N+10],dge[N+10],size[N+10];
    int f[N+10][N+10],C[N+10][N+10],Ans;
    bool L[N+10],Endl[N+10];
    void prepare(){
    	for (int i=0;i<=N;i++){
    		C[i][0]=1;
    		for (int j=1;j<=i;j++)	C[i][j]=(C[i-1][j]+C[i-1][j-1])%Mod;
    	}
    }
    int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
    bool merge(int x,int y){
    	x=find(x),y=find(y);
    	return x!=y?fa[x]=y,0:1;
    }
    void join(int x,int y){pre[++tot]=now[x],now[x]=tot,child[tot]=y;}
    void insert(int x,int y){join(x,y),join(y,x);}
    void dfs(int x,int fa){
    	f[x][1]=size[x]=1;
    	for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
    		if (son==fa)	continue;
    		dfs(son,x);
    		static int g[N+10];
    		for (int i=1;i<=size[x]+size[son];i++){
    			g[i]=0;
    			for (int j=1;j<=size[x];j++){
    				for (int k=1;k<=size[son];k++){
    					if (k-i+j<0)	continue;
    					g[i]=(g[i]+1ll*f[x][j]*f[son][k]%Mod*C[i-1][j-1]%Mod*C[j-1][k-i+j]%Mod)%Mod;
    				}
    			}
    		}
    		for (int i=1;i<=size[x]+size[son];i++)	f[x][i]=g[i];
    		size[x]+=size[son];
    	}
    }
    int main(){
    	prepare();
    	int n=read(),m=read();
    	for (int i=1;i<=n;i++)	fa[i]=i;
    	for (int i=1;i<=m;i++){
    		char ch[5];
    		scanf("%d%s%d",X+i,ch,Y+i);
    		L[i]=(ch[0]=='=');
    		if (L[i])	merge(X[i],Y[i]);
    	}
    	for (int i=1;i<=n;i++)	Endl[top[i]=find(i)]=1;
    	for (int i=1;i<=n;i++)	fa[i]=i;
    	for (int i=1;i<=m;i++){
    		if (L[i])	continue;
    		insert(top[X[i]],top[Y[i]]);
    		dge[top[Y[i]]]++;
    		if (merge(top[X[i]],top[Y[i]]))	return printf("0
    "),0;
    	}n++;
    	for (int i=1;i<n;i++)	if (Endl[i]&&!dge[i])	insert(n,i);
    	dfs(n,0);
    	for (int i=1;i<=size[n];i++)	Ans=(Ans+f[n][i])%Mod;
    	printf("%d
    ",Ans);
    	return 0;
    }
    
  • 相关阅读:
    配置Yaf
    计算机科学中最重要的32个算法
    mysql show status详解
    Structs 在Struts.xml中配置action时,action的name属性最好首字母大写
    MyEclipse创建ssh项目和连接数据库
    Myeclipse安装svn插件
    win7安装ubuntu双系统
    Java查看API和源码的方法
    华为oj平台的新网址
    详细解析Java中抽象类和接口的区别
  • 原文地址:https://www.cnblogs.com/Wolfycz/p/10253944.html
Copyright © 2011-2022 走看看