zoukankan      html  css  js  c++  java
  • CF140E New Year Garland

    题目

    CF140E New Year Garland

    分析

    计数dp常规好题。

    发现除了相邻两层的限制,我们对于每一层内部的计算是独立的。

    于是可以考虑先计算所有内部的情况,再通过容斥干掉相邻两层不能相邻的限制(即没有限制的情况数-两层相同的情况数)。

    对于每一层内部,我们考虑dp,设 (g[i][j]) 表示前 (i) 个小球,用了 (j) 种颜色的方案数。

    转移很显然是:(g[i][j]=g[i-1][j-1]+g[i-1][j] imes dbinom {j-1}1)

    含义为第 (i) 个小球可以是新的一种颜色,也可以是在之前已有的颜色当中选一个和上一个球不相同的颜色。

    然后考虑层之间的方案数计数,还是考虑dp。

    (f[i][j]) 表示前 (i) 层,第 (i) 层用了 (j) 个颜色的方案数。

    转移方程也很容易得出:(f[i][j]=dbinom mjsumlimits_{k=1}^{min(l[i-1],m)}f[i-1][k] imes g[l[i]][j]-f[i-1][j] imes g[l[i]][j])

    表示先不考虑限制,直接从前一个转移,减掉前一层和这一层相等的方案数。

    然后我们还要记得颜色是有标号的,所以要全局加个标号:(f[i][j]=j! imes (dbinom mjsumlimits_{k=1}^{min(l[i-1],m)}f[i-1][k] imes g[l[i]][j]-f[i-1][j] imes g[l[i]][j]))

    但是这样还没结束。

    首先,这里的组合数不太好算,因为模数不固定,我们要算只能 (exlucas) ,但是发现可以把外面的 (j!) 丢进去变成排列数,而且还是总个数固定的排列数,这个计算要方便的多。

    所以我们只需要预处理一下这些排列数和阶乘即可。

    其次,我们发现这里的转移是 (O(l)) 的,会T掉,直接前缀和优化一下就行了。

    接下来还会发现空间有点不够,但是我们知道 (f) 只会从前一个转移,而 (g) 是一直要用到的,所以可以把 (f) 滚动数组,并且排列数的那个数组很显然是一维的,因为总个数固定。

    然后就差不多了。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    //#ifdef ONLINE_JUDGE
    //	#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    //	char buf[1<<21],*p1=buf,*p2=buf;
    //#endif
    template<typename T>
    inline void read(T &x){
    	x=0;bool f=false;char ch=getchar();
    	while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
    	while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
    	x=f?-x:x;
    	return ;
    }
    template<typename T>
    inline void write(T x){
    	if(x<0) x=-x,putchar('-');
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    	return ;
    }
    #define ll long long
    #define ull unsigned long long
    #define ld long double
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define pc putchar
    #define PII pair<int,int>
    #define rep(i,x,y) for(register int i=(x);i<=(y);i++)
    #define dep(i,y,x) for(register int i=(y);i>=(x);i--)
    int MOD=100;
    inline int inc(int x,int y){x+=y;return x>=MOD?x-MOD:x;}
    inline int dec(int x,int y){x-=y;return x<0?x+MOD:x;}
    inline void incc(int &x,int y){x+=y;if(x>=MOD) x-=MOD;}
    inline void decc(int &x,int y){x-=y;if(x<0) x+=MOD;}
    inline void chkmin(int &x,int y){if(y<x) x=y;}
    inline void chkmax(int &x,int y){if(y>x) x=y;}
    const int N=5005,M=1e6+5,INF=1e9+7;
    int n,m,p,l[M],g[N][N],f[2][N],Am[N],fac[N],sum,tmp;
    signed main(){
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    //	ios::sync_with_stdio(false);
    //	double ST=clock();
    	read(n),read(m),read(p);MOD=p;
    	rep(i,1,n) read(l[i]);Am[0]=fac[0]=1;
    	rep(i,1,5000) Am[i]=1ll*Am[i-1]*(m-i+1)%MOD,fac[i]=1ll*fac[i-1]*i%MOD;g[0][0]=1;
    	rep(i,1,5000) rep(j,1,5000) g[i][j]=inc(g[i-1][j-1],1ll*g[i-1][j]*(j-1)%MOD);
    	sum=1;
    	rep(i,1,n){
    		tmp=0;
    		rep(j,l[i-1]+1,l[i]) f[(i&1)^1][j]=0;
    		rep(j,1,l[i]) f[i&1][j]=dec(1ll*sum*g[l[i]][j]%MOD*Am[j]%MOD,1ll*f[(i&1)^1][j]*g[l[i]][j]%MOD*fac[j]%MOD),incc(tmp,f[i&1][j]);
    		sum=tmp;
    	}
    	write(sum);
    //#ifndef ONLINE_JUDGE
    //	cerr<<"
    Time:"<<(clock()-ST)/CLOCKS_PER_SEC<<"s
    ";
    //#endif
    	return 0;
    }
    
    
  • 相关阅读:
    1046 Shortest Distance (20 分)(模拟)
    1004. Counting Leaves (30)PAT甲级真题(bfs,dfs,树的遍历,层序遍历)
    1041 Be Unique (20 分)(hash散列)
    1036 Boys vs Girls (25 分)(查找元素)
    1035 Password (20 分)(字符串处理)
    1044 Shopping in Mars (25 分)(二分查找)
    onenote使用小Tip总结^_^(不断更新中...)
    1048 Find Coins (25 分)(hash)
    三个故事
    领导者的举止
  • 原文地址:https://www.cnblogs.com/Akmaey/p/15477716.html
Copyright © 2011-2022 走看看