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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章