• 洛谷 AT2000 Leftmost Ball


    题目链接

    数据范围告诉我们需要一个(O(n^2))级别的做法

    首先朴素DP

    Step 1

    (dp[i][...])表示前(i)个球和每种颜色的球放了多少个

    显然我们必须要记录每种颜色的球放了多少才能完美解决,因此枚举放到第几个球是不行的

    Step 2

    (dp[..])表示每种颜色的球放了多少个

    因为白球是同种颜色球中最靠左的那个,为了方便和去重,我们考虑每个白球都是放在从左往右第一个空位,其他的球随意找空

    因为我们假定了每一种球是不同的,最后要除以((k-1))个球的排列

    Step 3

    (dp[i][j])表示当前放了(i)个白球,(j)种颜色的球

    因为白球一定是同种颜色的球最前面的那个

    我们把白球和其他的球分开来看

    对于不同颜色的球,我们发现放法就是从当前剩余的空位中找出来((k-1))个空的组合数

    因此可以考虑每次放一种颜色的球

    每次对序列操作,要么放一个白球,要么把一种颜色的(k-1)个球全部放入

    为了避免重复,我们每次只考虑从左往右第一个空位置上放什么球,

    [dp[i][j]=dp[i-1][j]+dp[i][j-1]*(n-j+1)*C_{n*k-(j-1)*(k-1)-i-1}^{k-2} ]

    解释一下:

    (dp[i-1][j])表示当前放了一个白球的转移

    (dp[i][j-1]*(n-j+1)*C_{n*k-(j-1)*(k-1)-i-1}^{k-2})表示当前放了一种颜色的球的转移

    其中(n-j+1)表示从剩下的颜色中选出一种颜色,放一个球在最前面的空位

    然后在(n*k-(j-1)*(k-1)-i-1)个空位中选出(k-2)个给剩下的球放

    注意(dp[i][j])要保证(i>=j)时才有意义

    [Code]

    #include<bits/stdc++.h>
    using namespace std;
    
    int read(){
    	int x=0; char c=getchar(); int flag=1;
    	while(!isdigit(c)) { if(c=='-') flag=-1; c=getchar(); }
    	while(isdigit(c)) { x=((x+(x<<2))<<1)+(c^48); c=getchar(); }
    	return x*flag;
    }
    
    const int mod=1e9+7;
    
    int n,k;
    int dp[2005][2005];//i个白球,j种颜色 
    
    int fac[4000005],Inv[4000005]; 
    
    int C(int n,int m){
    	if(m<0||n<m) return 0;
        return 1ll*(1ll*fac[n]*Inv[m]%mod)*Inv[n-m]%mod; 
    }
    
    void exgcd(int a,int b,int &x,int &y){
        if(b==0) { x=1; y=0; return ; }
        exgcd(b,a%b,y,x); y-=a/b*x;
    }
    
    int inv(int a){
        int x=0,y=0;
        exgcd(a,mod,x,y);
        return (x%mod+mod)%mod;
    }
    
    signed main(){
    	fac[0]=1;
    	for(int i=1;i<=4000000;i++) fac[i]=1ll*fac[i-1]*i%mod;
    	Inv[4000000]=inv(fac[4000000]);
    	for(int i=3999999;i>=0;i--) Inv[i]=1ll*Inv[i+1]*(i+1)%mod;
    	
        n=read(),k=read();
        if(k==1) { puts("1"); return 0; } 
    	dp[0][0]=1;
        
        for(int i=1;i<=n;i++){ 
    	    dp[i][0]=1;
    		for(int j=1;j<=i;j++){
    			dp[i][j]=(dp[i-1][j]+1ll*(1ll*dp[i][j-1]*C(n*k-(j-1)*(k-1)-i-1,k-2))%mod*(n-j+1)%mod)%mod; 
    		} 
    	}
    	
    	printf("%d
    ",dp[n][n]);
        return 0;
    }
    
    
  • 相关阅读:
    股票投资
    知道复利终值求本金
    复利计算和单利计算
    实验0:了解和熟悉操作系统
    0909我的编译原理感
    递归下降分析法
    有(很)穷的自动机
    评论
    C语言文法阅读与理解序
    C语言文法阅读与理解
  • 原文地址:https://www.cnblogs.com/zzhzzh123/p/12235545.html
Copyright © 2020-2023  润新知