• AtCoder agc013_d


    铜题………………(orz tzc没看题解AC了)

    洛谷题目页面传送门 & AtC题目页面传送门

    题意见洛谷。

    首先探索一个颜色序列的合法性。容易发现,合法当且仅当拿出每个球后,当前状态合法,即两种颜色的球的数量都非负。我们可以把盒子里的球的数量的每次变化都给整理出来(只需要实时记录红球(蓝球也可)的数量(cnt)即可,蓝球的数量就用总数减掉)。

    每一次操作一共分为三步:

    1. 拿出一个球,若是红球则减一,否则不变。此时总数为(n-1),要满足(cntgeq0,n-1-cntgeq0),即(cntin[0,n-1])
    2. 放入一红一蓝,(cnt)加一。此时不用满足啥,上一步合法这一步肯定也合法,所以归到下一步里一起结算;
    3. 拿出一个球,和上一步合起来的话,若是红球则不变,否则加一。此时总数为(n),要满足(cntgeq0,n-cntgeq0),即(cntin[0,n])

    不难想到,可以将这个变化过程做成一个折线,那么在(x)轴(时间轴)的每一处都有一个上下界,然后数合法的折线个数即可。这个很容易DP,设(dp_{i,j})为到第(i)步(一共(2m)步),当前的(y)值为(j)时合法折线数。目标:(sumlimits_{i=0}^ndp_{2m,i});由于起点(初始球数量分布情况)不定,所以边界是(forall iin[0,n],dp_{0,i}=1)。转移的话就很简单了,每一步的转移量都是常数级的。时间复杂度(mathrm O(nm))

    然后你认为,作为一道铜题,怎么可能这么水?于是带到第一个样例里手玩了一下,发现WA了……然后你就发现,上面那句“数合法的折线个数即可”就是在xjb扯淡。当两条折线起点不同,但是每一处的增长量都相同,即上下平移可重合时,它们代表的颜色序列是相同的。考虑去重。

    去重有一个惯用套路(不知为啥啥都可以被我总结成套路/yiw),就是把一些重复答案的不同处给限定一下,使得每个重复类里恰好有一个满足那个限定。本题中,限定方法非常显然,对于每个重复类,我们把每条折线都往下平移,直到接触到(x)轴了,再也不能往下平移了(因为每处的下界都是一样的,都是(0)),显然重复类里的每个折线都重合。如此一来,加上一个“接触(x)轴”这个限定,就完美去重了。

    如何实现?只需要在DP上加上第三维(kin{0,1}),表示到目前为止是否已经接触(x)轴。目标:(sumlimits_{i=0}^ndp_{2m,i,1}),边界:(forall iin[0,n],dp_{0,i,[i=0]}=1)。转移依然xjb转移。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=1000000007;
    const int N=3000,M=3000;
    int n,m;
    int dp[2*M+1][N+1][2];
    int main(){
    	cin>>n>>m;
    	for(int i=0;i<=m;i++)dp[0][i][i==0]=1;//边界 
    	for(int i=1;i<=m;i++)for(int j=0;j<=n;j++){//转移 
    		if(j==0){
    			(dp[2*i-1][j][1]+=dp[2*i-2][j][1])%=mod;
    			(dp[2*i-1][j][1]+=(dp[2*i-2][j+1][0]+dp[2*i-2][j+1][1])%mod)%=mod;
    			(dp[2*i][j][1]+=dp[2*i-1][j][1])%=mod;
    		}
    		else{
    			if(j<n){
    				(dp[2*i-1][j][0]+=dp[2*i-2][j][0])%=mod,(dp[2*i-1][j][1]+=dp[2*i-2][j][1])%=mod;
    				(dp[2*i-1][j][0]+=dp[2*i-2][j+1][0])%=mod,(dp[2*i-1][j][1]+=dp[2*i-2][j+1][1])%=mod;
    			}
    			(dp[2*i][j][0]+=dp[2*i-1][j][0])%=mod,(dp[2*i][j][1]+=dp[2*i-1][j][1])%=mod;
    			(dp[2*i][j][0]+=dp[2*i-1][j-1][0])%=mod,(dp[2*i][j][1]+=dp[2*i-1][j-1][1])%=mod;
    		}
    	}
    //	for(int j=n;~j;j--){for(int i=0;i<=2*m;i++)printf("(%2d,%2d) ",dp[i][j][0],dp[i][j][1]);puts("");}
    	int ans=0;
    	for(int i=0;i<=n;i++)(ans+=dp[2*m][i][1])%=mod;//目标 
    	cout<<ans;
    	return 0;
    }
    
  • 相关阅读:
    webpack-bundle-analyzer使用
    HTTP1.0,HTTP1.1和HTTP2.0区别
    document.readyState
    async和defer
    页面生命周期
    key的理解
    解释型语言和编译型语言
    AMD/CMD/CommonJS与ES6 Module的区别
    vue的keep-alive原理
    数字钱包metaplex-foundation
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/AtCoder-agc013-d.html
Copyright © 2020-2023  润新知