[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好像更好做一些。
代码:
这里讲一种利用了分治思想的乱搞方法。
先假设现在要求∑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; }
相关文章推荐
- BZOJ 4513: [Sdoi2016]储能表(记忆化搜索)
- [bzoj4513][SDOI2016]储能表——数位dp
- bzoj 4513: [Sdoi2016]储能表
- BZOJ 4513 [SDOI2016]储能表 【数位DP】
- BZOJ4513: [Sdoi2016]储能表
- [bzoj4513][SDOI2016]储能表
- BZOJ 4513 [Sdoi2016]储能表
- 搜索(四分树):BZOJ 4513 [SDOI2016 Round1] 储能表
- BZOJ 4513: [Sdoi2016]储能表 数位dp
- 【BZOJ4513】[Sdoi2016]储能表 数位DP
- BZOJ 4513: [Sdoi2016]储能表 [数位DP !]
- bzoj千题计划277:bzoj4513: [Sdoi2016]储能表
- bzoj 4513: [Sdoi2016]储能表 数位dp
- [BZOJ4513] [SDOI2016] 储能表 - 数位DP
- 【BZOJ4513】【Sdoi2016】储能表
- 【BZOJ4598】[Sdoi2016]模式字符串 树分治+hash
- 【bzoj4513】[Sdoi2016]储能表 数位dp
- 4513: [Sdoi2016]储能表
- [BZOJ4598][Sdoi2016]模式字符串(点分治+Hash)
- bzoj4598 [Sdoi2016]模式字符串(hash+点分治)