您的位置:首页 > 其它

[BZOJ4513][SDOI2016]储能表(数位DP/分治乱搞)

2017-11-19 21:49 330 查看
发现大家都用数位DP

这里讲一种利用了分治思想的乱搞方法。

先假设现在要求∑n−1i=0∑m−1j=0max((i xor j)−k,0)。设n>m。

先求得n−1在二进制意义下的位数,减1后记为x。分以下两种情况考虑:

(1)m≤2x
1ded7
。这时候将问题分为两个部分:

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。

此时递归到∑n−2x−1i=0∑m−1j=0max(i xor j,0),并且加上m(2x−k)(n−2x)。

(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,所以最后一部分实际上就是[0,n−2x),[0,m−2x)(递归处理)。而中间的两部分,可以通过k与2x的大小关系来分情况计算。和m≤2x的情况里计算第二部分的方法基本相似。

感觉数位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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: