2019牛客國慶one D.Modulo Nine dp

題目鏈接: https://ac.nowcoder.com/acm/contest/1099/D

題意:

你要構造一個長爲 nn 的數字串 a1a2a3a4....ana_1a_2a_3a_4....a_n,使得其滿足 mm 個條件,每個條件爲一個區間 [li,ri][l_i,r_i] 要求 aliali+1...aria_{l_i}*a_{l_i+1}*...*a_{r_i} modmod 9=09=0 ,問你能構造出多少這樣的串。

做法:

把題目剖析一下大概就是,在這些區間中的數字必須要至少有一個爲 00 或者分解質因數後有兩個 33 。那麼我們就可以把數字 3,63,6 當做數字 11 ,數字 9,09,0 當做數字 22 。即要求每個區間內的和要至少爲 22

隊友用的是記憶化搜索,大概意思 dp[i][j][k]dp[i][j][k] 是到達第 ii 個數字時,數字和爲 00 的第一個區間爲 kk ,和爲 11 的第一個區間爲 jj 。聽起來能懂的樣子然而…我是不會敲,膜一下膜一下…

後來看了人家的代碼學到了另一種稍微簡單一點的做法。

我們先維護好每一個右端點最大限制的左界在哪裏(即對於位置 pospos ,要求的最近的左區間位置在哪裏),同樣以上面的理論作爲基礎 dp[i][j][k]dp[i][j][k] 代表的是,到第 ii 個格子的時候,之前的第一個 11 出現的位置爲 jj ,第二個 11 出現的位置爲 k(k<=j)k(k<=j) 的方案數。

當然,我們需要一些前置條件限制,如果我們想要從 dp[i1][j][k]dp[i-1][j][k] 轉移來,那麼這個 kk 必要滿足 i=i1i=i-1 時候的情況。 如果已經滿足,那假設我們這一位放的是 363,6, 那麼最近的就可以變成 dp[i][i][j]dp[i][i][j] 即最近的有位置 ii 一份,假設我們這一位放的是 090,9, 那麼最近的就可以變成 dp[i][i][i]dp[i][i][i] ,即兩個最近的 11 都已經是我當前枚舉的位置了。然後就轉移就可以了。

代碼

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
using namespace std;
typedef long long ll;
const int N=55;
const ll mod=1e9+7;
ll dp[55][55][55],ans;
int n,m,L[55];
void add(ll &a,ll b){
    a=(a+b)%mod;
}
void init(){
    ans=0ll;
    memset(L,0,sizeof(L));
    memset(dp,0,sizeof(dp));
    dp[1][1][1]=2ll;dp[1][1][0]=2ll;
    dp[1][0][0]=6ll;
}
int main(){

    while(~scanf("%d%d",&n,&m)){
        init();
        rep(i,1,m){
            int x,y; scanf("%d%d",&x,&y);
            L[y]=max(L[y],x);
        }
        rep(i,2,n){
            rep(j,0,i){
                rep(k,0,j){
                    if(k<L[i-1]) continue;
                    add(dp[i][j][k],6ll*dp[i-1][j][k]);
                    add(dp[i][i][j],2ll*dp[i-1][j][k]);
                    add(dp[i][i][i],2ll*dp[i-1][j][k]);
                }
            }
        }
        rep(j,0,n){
            rep(k,0,j){
                if(k<L[n]) continue;
                add(ans,dp[n][j][k]);
            }
        }
        printf("%lld\n",ans);
    }

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章