zoukankan      html  css  js  c++  java
  • jzoj3424. 【NOIP2013模拟】粉刷匠

    Description

    赫克托是一个魁梧的粉刷匠,而且非常喜欢思考= =
    现在,神庙里有N根排列成一直线的石柱,从1到N标号,长老要求用油漆将这些石柱重新粉刷一遍。赫克托有K桶颜色各不相同的油漆,第i桶油漆恰好可以粉刷Ci根石柱,并且,C1+C2+C3…CK=N(即粉刷N根石柱正好用完所有的油漆)。长老为了刁难赫克托,要求相邻的石柱颜色不能相同。
    喜欢思考的赫克托不仅没有立刻开始粉刷,反而开始琢磨一些奇怪的问题,比如,一共有多少种粉刷的方案?
    为了让赫克托尽快开始粉刷,请你尽快告诉他答案。

    Input

    第一行一个正整数T,表示测试数据组数
    对于每一组测试数据数据:
    第1行:一个正整数K
    第2行:K个正整数,表示第i桶油漆可以粉刷的石柱个数,Ci。

    Output

    对于每组输入数据,输出一行一个整数,表示粉刷的方案数mod 1000000007。

    Sample Input

    3
    3
    1 2 3
    5
    2 2 2 2 2
    10
    1 1 2 2 3 3 4 4 5 5

    Sample Output

    10
    39480
    85937576

    Data Constraint

    30% N≤10, T≤5
    50% N≤15, T≤5
    80% K≤15,Ci≤5,T≤500
    100% K≤15,Ci≤6,T≤2000

    题解

    题外话
    话说这道题我在中考前几天还看到过。
    当时没什么想法,就没有想下去。
    今天一看到,突然灵机一动,发现是挺简单的一道题。
    调了好久,过了样例,交上去,结果TLE50.
    为什么呢?因为我比赛时把f数组开成了30000*100.
    然后2000每次memset一下,成功爆炸。

    考虑DP(话说暴力都不好拿分)
    我们按照每一种颜料依次来弄。
    那么加入一种颜料时,只需要考虑当前颜料与已经弄完的颜料的相对位置,不需要考虑染在哪根柱子上。
    意思就是,假如当前颜料是:ababbd
    加入3个c种颜料一种方式是:cacbabcbd
    那么我们设(f_{[i]})表示当前把前i 种颜料的序列做出来的方案数。
    可是一维的不够。因为题目要求是同种颜色不可以相邻。
    于是我们多加一维表示当前一共有j个位置满足前面的颜色与当前颜色相同。
    转移就有点神奇了。
    每次转移其实就相当于把k个新颜色的球插入原序列中。
    插入有方法——
    每次插入有可能插入到两个相同的球之间,也可能插入到两个不相同的球之间。
    所以,设有p个球插入到两个相同的球之间,有q个球插入到了两个不相同的球之间。
    答案分别是:(C_j^p*C_{sum-j+1}^q)
    但是还可能有别的球跟在上面两种球后面。
    这就相当于在m个不相同的盒子里放入n个相同的球的方案数。
    方案数即为(C_{n+m-1}^{n})(利用插板原理)
    答案即为(C_{k-1}^{k-p-q})

    标程

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <map>
    using namespace std;
    
    int maxn=32768,mo=1000000007;
    int n,t;
    int k[16];
    long long f[100][100],mi[16],c[100][100],sum[32768],len[32768];
    
    long long qsm(long long a,long long b)
    {
    	long long t=1;
    	long long y=a;
    	while (b>0)
    	{
    		if (b%2==1) t=(t*y)%mo;
    		y=(y*y)%mo;
    		b=b/2; 
    	}
    	return t;
    }
    
    int main()
    {
    	freopen("color.in","r",stdin);
    	for (int i=0;i<=100;i++)
    	{
    		c[i][0]=1;
    		for (int j=1;j<=i;j++)
    		{
    			c[i][j]=(c[i-1][j-1]+c[i-1][j])%mo;
    		}
    	}
    	
    	mi[0]=1;
    	for (int i=1;i<=15;i++)
    	{
    		mi[i]=mi[i-1]*2;
    	}
    	scanf("%d",&t);
    	while (t>0)
    	{
    		t--;
    		scanf("%d",&n);
    		for (int i=1;i<=n;i++)
    		{
    			scanf("%d",&k[i]);
    		}
    		memset(f,0,sizeof(f));
    		memset(sum,0,sizeof(sum));
    		memset(len,0,sizeof(len));
    		f[0][0]=1;
    			for (int i=1;i<=n;i++)
    			{
    					if (i==1)
    					{
    						f[i][k[i]-1]=1;
    						sum[i]=k[i];
    						len[i]=1;	
    					}
    					else
    					{
    						for (int j=0;j<=sum[i-1];j++) 
    						{
    							if (f[i-1][j]>0)
    							{ 
    								for (int p=0;p<=min(k[i],j);p++)
    								{
    									for (int q=0;q<=k[i]-p;q++)
    									{
    										if (q+p>=1)
    										{
    											f[i][j-p+k[i]-p-q]+=(f[i-1][j]*c[j][p]*c[sum[i-1]-j+1][q]*c[k[i]-1][k[i]-p-q])%mo;
    										}
    									}
    								}
    							}
    						}
    						sum[i]=sum[i-1]+k[i];
    						len[i]=len[i-1]+1;
    					}
    				
    			}
    		printf("%lld
    ",(f[n][0])%mo);
    	}
    }
    
    
  • 相关阅读:
    ubuntu基本配置学习(1)
    UITabBarController使用详解
    Could not find a storyboard named 'Main' in bundle NSBundle </Users/tianxiao/
    检查更新功能
    SDWebImage手动清除缓存的方法
    错误记录1
    如何获取path路径
    iOS如何获得本地Documents下的文件夹名称或文件名称
    重头系统的学习,不会咱就学!2014.6.18
    错误1
  • 原文地址:https://www.cnblogs.com/RainbowCrown/p/11285013.html
Copyright © 2011-2022 走看看