zoukankan      html  css  js  c++  java
  • 【BZOJ3167/4824】[Heoi2013]Sao/[Cqoi2017]老C的键盘

    【BZOJ3167】[Heoi2013]Sao

    Description

    WelcometoSAO(StrangeandAbnormalOnline)。这是一个VRMMORPG,含有n个关卡。但是,挑战不同关卡的顺序是一个很大的问题。有n–1个对于挑战关卡的限制,诸如第i个关卡必须在第j个关卡前挑战,或者完成了第k个关卡才能挑战第l个关卡。并且,如果不考虑限制的方向性,那么在这n–1个限制的情况下,任何两个关卡都存在某种程度的关联性。即,我们不能把所有关卡分成两个非空且不相交的子集,使得这两个子集之间没有任何限制。

    Input

    第一行,一个整数T,表示数据组数。对于每组数据,第一行一个整数n,表示关卡数。接下来n–1行,每行为“i sign j”,其中0≤i,j≤n–1且i≠j,sign为“<”或者“>”,表示第i个关卡必须在第j个关卡前/后完成。
    T≤5,1≤n≤1000

    Output

    对于每个数据,输出一行一个整数,为攻克关卡的顺序方案个数,mod1,000,000,007输出。

    Sample Input

    1
    10
    5 > 8
    5 > 6
    0 < 1
    9 < 4
    2 > 5
    5 < 9
    8 < 1
    9 > 3
    1 < 7

    Sample Output

    2580

    题解:憋了一下午想出来的树形DP题~

    如何设状态呢?显然应该是个二维的状态。设f[i][j]表示在i的子树中,i位于第j个位置的方案数。那么我们如何将x的当前状态与他的儿子的状态合并呢?

    先只考虑x<y的情况,我们设原来的siz[x]=sa,siz[y]=sb,sa+sb=sc,我们想用f[x][a]和f[y][b]来更新f[x][c](c>=a+b),为了区分新的f和旧的f,我们用g[c]表示新的f。

    如果x位于位置c,那么它左边有c-1个位置,相当于将y的序列中左边的数与x的序列中左边的数进行了二路归并,并且归并的顺序可以随便确定,那么方案数就是$C_{c-1}^{a-1}$,同理,右面的sc-c个数也可以归并处理,方案数是$C_{sc-c}^{sa-a}$,所以得到方程:

    $g[c]=sumlimits_{a=1}^{c}sumlimits_{b=1}^{c-a}C_{c-1}^{a-1}C_{sc-c}^{sa-a}f[x][a]*f[y][b]$

    可以将f[y][b]提出来

    $g[c]=sumlimits_{b=1}^{c}f[y][b]sumlimits_{a=1}^{c-b}C_{c-1}^{a-1}C_{sc-c}^{sa-a}f[x][a]$

    这个东西就可以用前缀和维护了~

    不过值得惭愧的是,我的代码的上界设的不紧,或是循环顺序不太对,复杂度其实应该是O(n^3)的,似乎可以改一改使得复杂度变成树形背包的优雅的O(n^2)。不过还是卡过了,就没有改,求不卡~不过处理4824那题还是很轻松的,因为是完全二叉树嘛~

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    typedef long long ll;
    const ll P=1000000007;
    int n,cnt;
    ll ans;
    ll C[1010][1010];
    ll f[1010][1010],g[1010],s[1010];
    int to[2010],next[2010],head[1010],val[2010],fa[1010],siz[1010];
    char str[5];
    void dfs(int x)
    {
    	int i,y,a,b,c;
    	siz[x]=1,f[x][1]=1;
    	for(i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[x])
    	{
    		y=to[i],fa[y]=x,dfs(y);
    		memset(g,0,sizeof(g[0])*(siz[x]+siz[y]+1));
    		if(val[i]==1)
    		{
    			for(c=2;c<=siz[x]+siz[y];c++)
    			{
    				s[max(1,c-siz[y])-1]=0;
    				for(b=1;b<=min(siz[x],c-1);b++)
    					s[b]=(s[b-1]+C[c-1][b-1]*C[siz[x]+siz[y]-c][siz[x]-b]%P*f[x][b])%P;
    				for(a=1;a<=min(siz[y],c-1);a++)
    				{
    					if(c-a>min(siz[x],c-1))	s[c-a]=s[min(siz[x],c-1)];
    					if(c-a<max(1,c-siz[y]))	s[c-a]=0;
    					g[c]=(g[c]+s[c-a]*f[y][a])%P;
    				}
    			}
    			f[x][1]=0;
    			for(c=2;c<=siz[x]+siz[y];c++)	f[x][c]=g[c];
    		}
    		else
    		{
    			for(c=2;c<=siz[x]+siz[y];c++)
    			{
    				s[max(1,c-siz[y])-1]=0;
    				for(b=1;b<=min(siz[x],c-1);b++)
    					s[b]=(s[b-1]+C[c-1][b-1]*C[siz[x]+siz[y]-c][siz[x]-b]%P*f[x][siz[x]-b+1])%P;
    				for(a=1;a<=min(siz[y],c-1);a++)
    				{
    					if(c-a>min(siz[x],c-1))	s[c-a]=s[min(siz[x],c-1)];
    					if(c-a<max(1,c-siz[y]))	s[c-a]=0;
    					g[c]=(g[c]+s[c-a]*f[y][siz[y]-a+1])%P;
    				}
    			}
    			f[x][siz[x]+siz[y]]=0;
    			for(c=1;c<siz[x]+siz[y];c++)	f[x][c]=g[siz[x]+siz[y]-c+1];
    		}
    		siz[x]=siz[x]+siz[y];
    	}
    }
    inline void add(int a,int b,int c)
    {
    	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
    }
    void work()
    {
    	scanf("%d",&n);
    	int i,a,b;
    	memset(head,-1,sizeof(head)),cnt=0;
    	memset(f,0,sizeof(f)),memset(fa,0,sizeof(fa));
    	for(i=1;i<n;i++)
    	{
    		scanf("%d%s%d",&a,str,&b),a++,b++;
    		if(str[0]=='>')	add(a,b,1),add(b,a,0);
    		if(str[0]=='<')	add(a,b,0),add(b,a,1);
    	}
    	dfs(1);
    	ans=0;
    	for(i=1;i<=n;i++)	ans=(ans+f[1][i])%P;
    	printf("%lld
    ",ans);
    }
    int main()
    {
    	int T,i,j;
    	C[0][0]=1;
    	for(i=1;i<=1000;i++)
    	{
    		C[i][0]=1;
    		for(j=1;j<=i;j++)	C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
    	}
    	scanf("%d",&T);
    	while(T--)	work();
    	return 0;
    }//5 10 5 > 8 5 > 6 0 < 1 9 < 4 2 > 5 5 < 9 8 < 1 9 > 3 1 < 7 10 6 > 7 2 > 0 9 < 0 5 > 9 7 > 0 0 > 3 7 < 8 1 < 2 0 < 4 10 2 < 0 1 > 4 0 > 5 9 < 0 9 > 3 1 < 2 4 > 6 9 < 8 7 > 1 10 0 > 9 5 > 6 3 > 6 8 < 7 8 > 4 0 > 6 8 > 5 8 < 2 1 > 8 10 8 < 3 8 < 4 1 > 3 1 < 9 3 < 7 2 < 8 5 > 2 5 < 6 0 < 9
    
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    typedef long long ll;
    const ll P=1000000007;
    int n,cnt;
    ll ans;
    ll C[1010][1010];
    ll f[1010][1010],g[1010],s[1010];
    int to[2010],next[2010],head[1010],val[2010],fa[1010],siz[1010];
    char str[5];
    void dfs(int x)
    {
    	int i,y,a,b,c;
    	siz[x]=1,f[x][1]=1;
    	for(i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[x])
    	{
    		y=to[i],fa[y]=x,dfs(y);
    		memset(g,0,sizeof(g[0])*(siz[x]+siz[y]+1));
    		if(val[i]==1)
    		{
    			for(c=2;c<=siz[x]+siz[y];c++)
    			{
    				s[max(1,c-siz[y])-1]=0;
    				for(b=1;b<=min(siz[x],c-1);b++)
    					s[b]=(s[b-1]+C[c-1][b-1]*C[siz[x]+siz[y]-c][siz[x]-b]%P*f[x][b])%P;
    				for(a=1;a<=min(siz[y],c-1);a++)
    				{
    					if(c-a>min(siz[x],c-1))	s[c-a]=s[min(siz[x],c-1)];
    					if(c-a<max(1,c-siz[y]))	s[c-a]=0;
    					g[c]=(g[c]+s[c-a]*f[y][a])%P;
    				}
    			}
    			f[x][1]=0;
    			for(c=2;c<=siz[x]+siz[y];c++)	f[x][c]=g[c];
    		}
    		else
    		{
    			for(c=2;c<=siz[x]+siz[y];c++)
    			{
    				s[max(1,c-siz[y])-1]=0;
    				for(b=1;b<=min(siz[x],c-1);b++)
    					s[b]=(s[b-1]+C[c-1][b-1]*C[siz[x]+siz[y]-c][siz[x]-b]%P*f[x][siz[x]-b+1])%P;
    				for(a=1;a<=min(siz[y],c-1);a++)
    				{
    					if(c-a>min(siz[x],c-1))	s[c-a]=s[min(siz[x],c-1)];
    					if(c-a<max(1,c-siz[y]))	s[c-a]=0;
    					g[c]=(g[c]+s[c-a]*f[y][siz[y]-a+1])%P;
    				}
    			}
    			f[x][siz[x]+siz[y]]=0;
    			for(c=1;c<siz[x]+siz[y];c++)	f[x][c]=g[siz[x]+siz[y]-c+1];
    		}
    		siz[x]=siz[x]+siz[y];
    	}
    }
    inline void add(int a,int b,int c)
    {
    	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
    }
    void work()
    {
    	scanf("%d",&n);
    	int i,a,b;
    	memset(head,-1,sizeof(head)),cnt=0;
    	memset(f,0,sizeof(f)),memset(fa,0,sizeof(fa));
    	for(i=1;i<n;i++)
    	{
    		scanf("%d%s%d",&a,str,&b),a++,b++;
    		if(str[0]=='>')	add(a,b,1),add(b,a,0);
    		if(str[0]=='<')	add(a,b,0),add(b,a,1);
    	}
    	dfs(1);
    	ans=0;
    	for(i=1;i<=n;i++)	ans=(ans+f[1][i])%P;
    	printf("%lld
    ",ans);
    }
    int main()
    {
    	int T,i,j;
    	C[0][0]=1;
    	for(i=1;i<=1000;i++)
    	{
    		C[i][0]=1;
    		for(j=1;j<=i;j++)	C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
    	}
    	scanf("%d",&T);
    	while(T--)	work();
    	return 0;
    }//5 10 5 > 8 5 > 6 0 < 1 9 < 4 2 > 5 5 < 9 8 < 1 9 > 3 1 < 7 10 6 > 7 2 > 0 9 < 0 5 > 9 7 > 0 0 > 3 7 < 8 1 < 2 0 < 4 10 2 < 0 1 > 4 0 > 5 9 < 0 9 > 3 1 < 2 4 > 6 9 < 8 7 > 1 10 0 > 9 5 > 6 3 > 6 8 < 7 8 > 4 0 > 6 8 > 5 8 < 2 1 > 8 10 8 < 3 8 < 4 1 > 3 1 < 9 3 < 7 2 < 8 5 > 2 5 < 6 0 < 9
  • 相关阅读:
    libevent-2.0.so.5 (安装MEMCACHED问题)
    MySQL的show语句大全
    远程客户端连接MysqL数据库太慢解决方案
    用SQL命令查看Mysql数据库大小
    elasticsearch-head安装及启动
    logstash收集IIS日志
    备份并删除7天以前数据
    shell脚本递归压缩实践
    二目运算符 “->”
    关于int main( int argc, char *argv[] )
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7605703.html
Copyright © 2011-2022 走看看