• [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
    ",ans) ;
    	}
    	return 0 ;
    }
    
  • 相关阅读:
    java核心学习(八) 枚举类
    java核心学习(七) 内部类、匿名内部类、Lambda表达式
    算法-快速排序
    java核心学习(六) 面向接口编程
    java核心学习(五) 修饰符(重点是static、final)
    java 核心学习笔记(四) 单例类
    贪心 zoj3197
    贪心 poj3045
    三分 POJ3737
    浮点数二分答案 HDU1969
  • 原文地址:https://www.cnblogs.com/beretty/p/10450266.html
Copyright © 2020-2023  润新知