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;
    }
    
    
  • 相关阅读:
    解题:HNOI 2008 玩具装箱
    2016级算法第一次上机助教版解题报告
    求解斐波那契数列复杂度分析
    数据库复习之规范化理论应用(第八次上机内容)
    数据库复习之规范化理论
    题目1042:Coincidence(最长公共子序列)
    题目1020:最小长方形(简单)
    题目1016:火星A+B(字符串拆分)
    题目1014:排名(结构体排序)
    题目1021:统计字符(hash简单应用)
  • 原文地址:https://www.cnblogs.com/Akmaey/p/15477716.html
Copyright © 2011-2022 走看看