[SDOI2016]储能表

[SDOI2016]储能表

题解

数位DP似乎正解是找i^j的龟绿
在二进制上做数位\(DP\),需要同时满足\(n,m,k\)三个限制条件
那么设\(f[i][0/1][0/1][0/1]\)表示当前到从前往后的第i位,到这一位的位置时是否卡\(n\)上界,是否卡\(m\)的上界,是否卡\(k\)的下界的异或和,\(g[i][0/1][0/1][0/1]\)表示方案数
因为如果异或和小于k就变成0了,那么我们只需要考虑异或和\(>=k\)的数,然后最后再把\(f[]\)的答案减去\(k*g[]\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
# define int long long
const int M = 75 ;
using namespace std ;

inline int read() {
    char c = getchar() ; int x = 0 , w = 1 ;
    while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
    while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
    return x*w ;
}

bool wn[M] , wm[M] , wk[M] ;
int n , m , k , mod , len ;
int f[M][2][2][2] , g[M][2][2][2] , ans ;
void dfs(int pos , bool upn , bool upm , bool kp) { 
//  当前位数,是否卡n的上界,是否卡m的上界,是否卡k的下界
    if(g[pos][upn][upm][kp]) return ;
    if(pos > len) {
        f[pos][upn][upm][kp] = 0 ; 
        g[pos][upn][upm][kp] = 1 ;
        return ; 
    }
    int ret1 , ret2 , rn = (upn ? wn[pos] : 1) ,
    rm = (upm ? wm[pos] : 1) , kw = wk[pos] ;
    for(int i = 0 ; i <= rn ; i ++)
        for(int j = 0 ; j <= rm ; j ++) {
            if(kp && ((i ^ j) < kw)) continue ;
            dfs(pos + 1 , (upn & (i == rn)) , (upm & (j == rm)) , (kp & ((i ^ j) == kw))) ;
            ret1 = g[pos + 1][(upn & (i == rn))][(upm & (j == rm))][(kp & ((i ^ j) == kw))] % mod ;
            ret2 = f[pos + 1][(upn & (i == rn))][(upm & (j == rm))][(kp & ((i ^ j) == kw))] % mod ;
            g[pos][upn][upm][kp] = (g[pos][upn][upm][kp] + ret1) % mod ;
            f[pos][upn][upm][kp] = (f[pos][upn][upm][kp] + ((i ^ j) << (len - pos)) % mod * ret1 % mod + ret2) % mod ;
        }
}
# undef int
int main() {
# define int long long
    int T = read() ; 
    while(T --) {
        memset(wn , false , sizeof(wn)) ; memset(wm , false , sizeof(wm)) ;
        memset(wk , false , sizeof(wk)) ; len = 0 ;
        memset(f , 0 , sizeof(f)) ; memset(g , 0 , sizeof(g)) ;
        n = read() - 1 ; m = read() - 1 ; k = read() ; mod = read() ;
        for(int i = 0 ; i <= 60 ; i ++) 
            if((n & (1ull << i)) || (m & (1ull << i)) || (k & (1ull << i)))
                len = i + 1 ;
        for(int i = 1 ; i <= len ; i ++) {
            wn[i] = (n & (1ull << (len - i))) ;
            wm[i] = (m & (1ull << (len - i))) ;
            wk[i] = (k & (1ull << (len - i))) ;
        }
        dfs(1 , true , true , true) ;
        ans = ((f[1][1][1][1] % mod - k % mod * g[1][1][1][1] % mod) % mod + mod) % mod ;
        printf("%lld\n",ans) ;
    }
    return 0 ;
}
上一篇:P4075 [SDOI2016]模式字符串


下一篇:luogu P4076 [SDOI2016]墙上的句子