[SDOI2016]储能表

题意

link
给出 \(T, n, m, k, p, (n, m, k \leq 10^{18}, p \leq 10^9)\), 有 \(T\) 组询问。
求 $$\sum_{i = 0}^{n - 1}\sum_{j = 0}^{m - 1} \max((i \oplus j) - k, 0)$$

数位dp

状态

实际上能求出所有 \(i \oplus j > k\) 的和 \(f\),和个数 \(g\),那么答案就是 \(f - g \times k\)

由于是异或,可以分开考虑二进制下每一位。

考虑求出 \(g\), 设 \(g(n, 1/0, 1/0, 1/0)\) 表示现在是第 \(n\) 位 (低到高),是否到 \(n\) 的上界,是否到 \(m\) 的上界,是否到 \(k\) 的下界的个数。

\(f\) 同理,只不过记得是和。

初始和最终状态

这里我是从高位到低位递归,对于最低位 \(g_{0, x, y, z}\),由于要满足 \(< n, < m, > k\) 的限制,

只有 \(g(0, 0, 0, 0) = 1\)

最后要求的是 \(g(64, 1, 1, 1)\)

转移

到了上界就有 \(n, m\) 在这一位的限制,否则就没有限制, \(k\) 同理。

枚举两个数在这一位的取值,就能转移了。

转移就懒得写了太多了,看代码。

对于 \(f(i, x, y, z)\), 求出 \(g\) 转移到这一位的方案数 乘上位权即可。

分析

状态很少 \(O(62 * 2 * 2 * 2)\), 转移也是 \(O(1)\) 级别的。

用一下记忆化搜索就很快了。

时间复杂度大概是 \(O(T \log n)\)

代码

记得取模,求答案时对k也要。

#include<bits/stdc++.h>

using namespace std;

using ll = long long;
const int MAXN = 65;
const int INF = 0x7fffffff;
//const int mod = 1000000007;
int mod; 
const double eps = 1e-9; 

template <typename T>
void Read(T &x) {
	x = 0; T f = 1; char a = getchar();
	for(; a < '0' || '9' < a; a = getchar()) if (a == '-') f = -f;
	for(; '0' <= a && a <= '9'; a = getchar()) x = (x * 10) + (a ^ 48);
	x *= f;
}

inline int add(const int &a, const int &b) { 
	static int c;
	c = a + b;
	if (c >= mod) c -= mod;
	if (c < 0) c += mod;
	return c;   
} 
inline int mul(const int &a, const int &b) {
	return 1ll * a * b % mod; 
}
int qpow(int a, int b) {
	int sum(1);
	while(b) {
		if (b & 1) sum = mul(sum, a);
		a = mul(a, a);
		b >>= 1;
	}
	return sum; 
}

ll n, m, k;
int f[MAXN][2][2][2]; 
int F(int N, int a, int b, int c) {
	if (N == -1) return !a && !b && !c; 
	int nn = (n >> N) & 1, mm = (m >> N) & 1, kk = (k >> N) & 1, sum = 0;
	
	if (~f[N][a][b][c]) return f[N][a][b][c]; 
	for (int x = 0; x <= 1; x ++) 
		for (int y = 0; y <= 1; y ++) {
			int z = x ^ y;
			if ((a && x > nn) || (b && y > mm) || (c && (z < kk)))
				continue; 
			int A = a & (x == nn), B = b & (y == mm), C = c & (z == kk);
			sum = add(sum, F(N - 1, A, B, C));  
		}
	return f[N][a][b][c] = sum; 
}

int g[MAXN][2][2][2]; 
int G(int N, int a, int b, int c) {	
	if (N == -1) return 0; 
	int nn = (n >> N) & 1, mm = (m >> N) & 1, kk = (k >> N) & 1, sum = 0;
	if (~g[N][a][b][c]) return g[N][a][b][c]; 
	for (int x = 0; x <= 1; x ++) 
		for (int y = 0; y <= 1; y ++) {
			int z = x ^ y;
			if ((a && x > nn) || (b && y > mm) || (c && (z < kk)))
				continue; 
			int A = a & (x == nn), B = b & (y == mm), C = c & (z == kk);
			sum = add(sum, G(N - 1, A, B, C));
			sum = add(sum, mul(F(N - 1, A, B, C), (z ? qpow(2, N) : 0)));  
		}
	return g[N][a][b][c] = sum; 
}
int main() {
	int T;
	cin >> T;
	while(T --) {
		cin >> n >> m >> k >> mod;
		memset(f, -1, sizeof(f));
		memset(g, -1, sizeof(g));
		cout << add(G(62, 1, 1, 1), -mul(F(62, 1, 1, 1), k % mod)) << endl;
	}
	return 0;
} 
posted @ 2022-02-28 22:08  qjbqjb  阅读(33)  评论(0编辑  收藏  举报