• 【题解】At2370 Piling Up


    【题解】At2370 Piling Up

    [ dp(i,j,0/1) \ 正在进行i项操作并且此时黑球剩下j个,黑球[0/1]数量曾经到过0 \ 为什么加第二位,判重。怎么想到的? ]

    非常神仙了。现在我做题基本上就是改编戏说了...自己是做不出来的,不管是只是层面还是思维层面的高度都不够。

    那就一题一题地吸收吧。

    其实问这样的$dp$设计是如何想到的,都是人们看清楚了方案重合的本【题解】At2370 Piling Up质是什么,然后就针对这个本质设计来的...

    感觉这种设$0/1$有一定的代表性,图文解释一下

    假设蓝色的线是黑球的数量关于$t$的变化关系函数,可以知道同样形状的线拿出来的样子是一样的,但是如果直接$dp(i,j)$没有$0 / 1$就会把图中四根线代表的颜色序列当做不一样的,这样就重复了。

    有什么办法可以避免呢?我们可以发现,同样的走的曲线一定是将整个坐标系排满了,意思是排到无法再加入新的同样走的曲线进去了,如果要无法再加曲线,那么一定有一根曲线曾经到过坐标轴(黑线)。这是解决重复的关键,也是问题的本质。

    也就是说,我们标识一个$dp(i,j)$是否曾经到过底部,就可以防止这样的重复了。

    你肯定发现问题了,一定存在这样的排颜色方案使得黑球数量从没有为0。不过我们钦定一条在$i=0$处变为$0$的曲线就好了。 考虑怎么转移,分类讨论:

    • 白白
    • 黑黑
    • 白黑
    • 黑白

    转移很好转移(也没有好吗!这种转移对我来说就相当于做第二道题目了!就是那种神仙学长都觉得很显然但是我要折寿地去推导啊啊啊啊啊啊啊QAQQAQ)

    抄的代码,wk1自己写的(不对),wk2是题解

    #include<bits/stdc++.h>
    
    using namespace std;typedef long long ll;
    #define DRP(t,a,b) for(register int t=(a),edd=(b);t>=edd;--t)
    #define RP(t,a,b)  for(register int t=(a),edd=(b);t<=edd;++t)
    #define ERP(t,a)   for(register int t=head[a];t;t=e[t].nx)
    #define midd register int mid=(l+r)>>1
    #define TMP template < class ccf >
    #define lef l,mid,pos<<1
    #define rgt mid+1,r,pos<<1|1
    #define pushup(pos) (seg[pos]=seg[pos<<1]+seg[pos<<1|1])
    TMP inline ccf qr(ccf b){
        register char c=getchar();register int q=1;register ccf x=0;
        while(c<48||c>57)q=c==45?-1:q,c=getchar();
        while(c>=48&&c<=57)x=x*10+c-48,c=getchar();
        return q==-1?-x:x;}
    TMP inline ccf Max(ccf a,ccf b){return a<b?b:a;}
    TMP inline ccf Min(ccf a,ccf b){return a<b?a:b;}
    TMP inline ccf Max(ccf a,ccf b,ccf c){return Max(a,Max(b,c));}
    TMP inline ccf Min(ccf a,ccf b,ccf c){return Min(a,Min(b,c));}
    TMP inline ccf READ(ccf* _arr,int _n){RP(t,1,_n)_arr[t]=qr((ccf)1);}
    //----------------------template&IO---------------------------
    const int maxn=3e3+15;
    const ll mod=1e9+7;
    ll dp[maxn][maxn][2];
    ll ans;
    #define md(x) ((x)%mod)
    #define add(x,y) ((x)=md((x)+(y)))
    
    int n,m;
    inline void wk(){
        dp[1][0][1]=dp[1][1][1]=1;
        RP(t,1,n) dp[1][t][0]=2;
        RP(t,1,m){
    	RP(i,0,n){
    	    //取两个白
    	    if(i<n){
    		if(i){
    		    dp[t+1][i+1][0]=md(dp[t+1][i+1][0]+dp[t][i][0]);
    		    dp[t+1][i+1][1]=md(dp[t+1][i+1][1]+dp[t][i][1]);
    		}
    		else dp[t+1][i+1][1]=md(dp[t+1][i+1][1]+dp[t][i][0]+dp[t][i][1]);
    	    }
    	    //取两个黑色
    	    if(i){
    		if(i-1){
    		    dp[t+1][i-1][0]=md(dp[t+1][i-1][0]+dp[t][i][0]);
    		    dp[t+1][i-1][1]=md(dp[t+1][i-1][1]+dp[t][i][1]);
    		}
    		else dp[t+1][i-1][1]=md(dp[t+1][i-1][1]+dp[t][i][0]+dp[t][i][1]);
    	    }
    	    //一黑一白
    	    if(i){
    		dp[t+1][i][0]=md(dp[t+1][i][0]+(dp[t][i][0]<<1));
    		dp[t+1][i][1]=md(dp[t+1][i][1]+(dp[t][i][1]<<1));
    	    }
    	    else dp[t+1][i][1]=md(dp[t+1][i][1]+dp[t][i][0]);
    	}
        }
    }
    
    inline void wk2(){
        
        dp[1][0][1]=1;
        RP(t,1,n) dp[1][t][0]=1;
        RP(t,1,m){
    	RP(i,0,n){
    	    if(i){
    		add(dp[t+1][i-1][1],dp[t][i][1]);
    		add(dp[t+1][i][1],dp[t][i][1]);
    		if(i==1) add(dp[t+1][i-1][1],dp[t][i][0]),add(dp[t+1][i][1],dp[t][i][0]);
    		else add(dp[t+1][i-1][0],dp[t][i][0]),add(dp[t+1][i][0],dp[t][i][0]);
    		
    	    }
    	    if(i<n){
    		add(dp[t+1][i+1][0],dp[t][i][0]);
    		add(dp[t+1][i+1][1],dp[t][i][1]);
    		
    		add(dp[t+1][i][0],dp[t][i][0]);
    		add(dp[t+1][i][1],dp[t][i][1]);
    	    }
    	}
        }
    }
    
    int main(){
        
        n=qr(1);m=qr(1);
        wk2();    
        RP(t,0,n) ans=md(ans+dp[m+1][t][1]);
        cout<<ans<<endl;
        return 0;
    }
    
    
  • 相关阅读:
    权限管理
    Linux常用命令
    SSM的开发步骤分析
    03每日课后作业面试题汇总
    Redis常用命令
    大觅网07day
    消息队列面试题
    bzoj 3745: [Coci2015]Norma
    Codeforces 343E Pumping Stations
    UOJ #236. 【IOI2016】railroad
  • 原文地址:https://www.cnblogs.com/winlere/p/10472232.html
Copyright © 2020-2023  润新知