ZROI278 序列II 【数位dp】【状态压缩】

题意:对于一个序列 a ,有:
| a | = m
a i [ 0 , 9 ]
我们可以将每个序列 a 的前缀 a 1.. i 看做一个可能含前导零的十进制数。
对于每一个 r [ 0 , 6 ] ,都存在至少一个 i [ 1 , m ] 使得 a 1.. i 写成的十进制数 m o d   7 = r
给出 m   p ,求方案数 m o d   p 的值。

题解:
还有不到100天就要 NOIP 了,鉴于 NOIP2017 的惨痛教训,游记我是一定会写的,希望到时候自己能有一个很大的飞跃吧。
模拟赛现场AC简直不能更赞 ~~
d p [ i ] [ j ] [ l ] 表示考虑到第 i 位,模 7 状态为 j 1.. ( i 1 ) 所组成的数模 7 为 l 的方案数
比如: j = ( 0010100 ) 2 ,那么当前状态(前缀和目前)已经模 7 = 3   a n d   5
转移:
d p [ i ] [ j ] [ l ] => d p [ i + 1 ] [ j | ( ( 1 << ( l 10 + k ) )
其中 k = 0...9
解释:枚举当前位,那么 1.. i 所组成的数模 7 就是 ( l 10 + k )

一开始一直困扰我的是如何记录前缀,后来才想明白我可以多开一维啊233333

代码:

// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() puts("okkkkkkkk")

using namespace std;

typedef long long LL;

const int inf = 1 << 30;

const int maxn=1e3+5;
int m,mod;
int dp[maxn][(1<<7)+5][8];
//int qz[maxn][(1<<7)+5];

int main(){
    //freopen("C.in","r",stdin);
    scanf("%d%d",&m,&mod);
    dp[m+1][0][0]=1;
    for(int i=m;i>=1;i--){
        for(int j=0;j<=(1<<7)-1;j++){
            for(int k=0;k<=9;k++){
                for(int l=0;l<7;l++){
                    (dp[i][j|(1<<((l*10+k)%7))][(l*10+k)%7]+=dp[i+1][j][l])%=mod;
                }
            }
        }
    }
    int ans=0;
    for(int i=0;i<7;i++)(ans+=dp[1][(1<<7)-1][i])%=mod;
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_34896694/article/details/81461433