zoukankan      html  css  js  c++  java
  • [csa round#1]Number Elimination——动态规划+计数

    题目大意:

    你有(n)个方块排成一排,每个方块有一个权值(a_i),你每次可以选择一个二元组((x,y) x<y),并消除x和y中权值较小的那个方块,如果二者权值相同则消除标号较小的那个,产生(max(a_x,a_y))的费用。你每次选择的二元组中不能选择已经被消除的方块。最后这一排方块只会剩下一个,游戏目标是使费用最少。
    求可以使费用最小的方案数。

    思路:

    为了使费用最小,可以发现相同的数一定是要在一起消,直到只剩一个。
    同时对于不同的数之间,我们要消除一个数的话,这个数必定只剩下一个,并且比这个数小的数都要在之前消完。
    不同的种类一定是从小到大的顺序消完,于是我们设dp[i]为前i个种类消完的方案数,每一次将一个种类添加进去来转移。
    这里需要预处理一个种类自身消除的方案数(f[i])(i为集合大小)(=f[i-1] imes i imes (i-1)/2)
    于是在dp[i]的时候枚举第i个种类的物品在i-1种最后一个消完之前消完的数量j,用隔板法来计算前j个放置的位置,可得dp方程为:

    [dp_{i}=sum_{j=0}^{sz_i-1} imes f_{i-1} imes {sum_{i-1}+j-1choose j} imes(sz_i-j) ]

    直接转移即可。

    #include<bits/stdc++.h>
    
    #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
    #define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
    #define debug(x) cout<<#x<<"="<<x<<endl
    typedef long long ll;
    
    using namespace std;
    
    void File(){
    	freopen("out.in","r",stdin);
    	freopen("out.out","w",stdout);
    }
    
    template<typename T>void read(T &_){
    	T __=0,mul=1; char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-')mul=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    	_=__*mul;
    }
    
    const int maxn=1e5+10;
    const ll mod=1e9+7;
    int n,a[maxn],b[maxn],tot,cnt[maxn];
    ll fac[maxn],ifac[maxn],f[maxn],dp[maxn],sum[maxn];
    
    ll qpow(ll x,ll y){
    	ll ret=1; x%=mod;
    	while(y){
    		if(y&1)ret=ret*x%mod;
    		x=x*x%mod;
    		y>>=1;
    	}
    	return ret;
    }
    
    ll C(ll x,ll y){return fac[x]*ifac[y]%mod*ifac[x-y]%mod;}
    
    int main(){
    //	File();
    	read(n);
    	REP(i,1,n)read(a[i]),b[++tot]=a[i];
    
    	sort(b+1,b+tot+1);
    	tot=unique(b+1,b+tot+1)-b-1;
    	REP(i,1,n)a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
    
    	fac[0]=1;
    	REP(i,1,n)fac[i]=fac[i-1]*i%mod;
    	ifac[n]=qpow(fac[n],mod-2);
    	DREP(i,n-1,0)ifac[i]=ifac[i+1]*(i+1)%mod;
    
    	REP(i,1,n)++cnt[a[i]];
    
    	f[0]=1;
    	ll inv2=qpow(2,mod-2);
    	REP(i,1,n)f[i]=f[i-1]*i%mod*(i+1)%mod*inv2%mod;
    
    	dp[1]=f[cnt[1]-1];
    	REP(i,2,tot){
    		sum[i-1]=sum[i-2]+cnt[i-1];
    		REP(j,0,cnt[i]-1)
    			dp[i]=(dp[i]+dp[i-1]*C(sum[i-1]+j-1,j)%mod*(cnt[i]-j)%mod)%mod;
    		dp[i]=dp[i]*f[cnt[i]-1]%mod;
    	}
    
    	printf("%lld
    ",dp[tot]);
    	return 0;
    }
    
  • 相关阅读:
    Java Web工作原理
    Java——入门“HelloWorld”
    Java——介绍
    vscode给 vue 项目 添加 eslint 验证提示遇到的细节问题
    使用全局变量,当多个线程同时修改静态属性第一季
    自编验证码图片识别程序 自定义的FloodFill函数
    nginx卸载ssl django明文
    nginx 7层全转发
    JAVA Socket入门详解(基本用法与代码实现)
    小白进入公司究竟有哪些不同?学生一定要看!
  • 原文地址:https://www.cnblogs.com/ylsoi/p/9838713.html
Copyright © 2011-2022 走看看