1. 程式人生 > >[SDOI2016]儲能表

[SDOI2016]儲能表

ans ems string mage alt src 條件 答案 char

技術分享圖片

題解

數位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 ;
}

[SDOI2016]儲能表