发现大家都用数位DP
这里讲一种利用了分治思想的乱搞方法。
先假设现在要求
∑n−1i=0∑m−1j=0max((i xor j)−k,0)
。设
n>m
。
先求得
n−1
在二进制意义下的位数,减
1
后记为
(1)
m≤2x
。这时候将问题分为两个部分:
1、
[0,2x)
内的数与
[0,m)
内的数互相异或。
2、
[2x,n)
内的数与
[0,m)
内的数互相异或。
对于第一个部分,由于
a xor b=c
时
a xor c=b
,并且
m≤2x
,所以
[0,2x)
内的数异或上任意一个
[0,m)
内的数后,得到的结果仍然是
[0,2x)
。
因此第一部分的答案为
m∑2x−k−1i=0i
,
∑2x−k−1i=0i
可以用公式计算。
第二部分则递归处理(
[2x,n)
内的数与
[0,m)
内的数互相异或相当于
[0,n−2x)
内的数与
[0,m)
内的数互相异或再加
2x
),但要分两种情况:
1、
k≥2x
,则容易得到,递归到
∑n−2x−1i=0∑m−1j=0max((i xor j)−(k−2x),0)
。
2、
k<2x
,第二部分所有的
i xor j
都不会小于
k
。
此时递归到
(2)
m>2x
。此时分为四个部分(互相异或):
1、
[0,2x),[0,2x)
。
2、
[2x,n),[0,2x)
。
3、
[0,2x),[2x,m)
。
4、
[2x,n),[2x,m)
。
容易推出,第一部分的结果为
2x∑2x−k−1i=0
,第四部分的结果由于最高位被异或掉就变成了
0
,所以最后一部分实际上就是
感觉数位DP好像更好做一些。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int PYZ;
typedef long long ll;
int sum_x(ll n) {
if (n == -1) return 0;
if (n & 1) return 1ll * (n % PYZ) * ((n + 1 >> 1) % PYZ) % PYZ;
else return 1ll * ((n >> 1) % PYZ) * ((n + 1) % PYZ) % PYZ;
}
int sum(ll l, ll r) {
if (l > r) return 0;
return (sum_x(r) - sum_x(l - 1) + PYZ) % PYZ;
}
int solve(ll n, ll m, ll K) {
if (n < m) swap(n, m);
if (m == 1) return sum(0, n - 1 - K);
ll x = n - 1; int tot = 0, res; while (x) tot++, x >>= 1;
ll mid = 1ll << tot - 1;
if (m <= mid) {
res = 1ll * (m % PYZ) * sum(0, mid - 1 - K) % PYZ;
if (K >= mid) (res += solve(n - mid, m, K - mid)) %= PYZ;
else {
(res += solve(n - mid, m, 0)) %= PYZ;
(res += 1ll * ((mid - K) % PYZ) * ((n - mid) % PYZ) % PYZ
* (m % PYZ) % PYZ) %= PYZ;
}
return res;
}
res = 1ll * (mid % PYZ) * sum(0, mid - 1 - K) % PYZ;
(res += solve(n - mid, m - mid, K)) %= PYZ; x = (n - mid + m - mid) % PYZ;
if (K <= mid) {
(res += 1ll * x * sum(0, mid - 1) % PYZ) %= PYZ;
(res += 1ll * x * (mid % PYZ) % PYZ *
((mid - K) % PYZ) % PYZ) %= PYZ;
}
else (res += 1ll * x * sum(0, (mid << 1) - 1 - K) % PYZ) %= PYZ;
return res;
}
void work() {
ll n, m, K; scanf("%lld%lld%lld%d", &n, &m, &K, &PYZ);
printf("%d\n", solve(n, m, K));
}
int main() {
int T; cin >> T; while (T--) work();
return 0;
}