题解-CF809C Find a car[*easy]
题解区里面写的都是数位 \(dp\),蒟蒻来一个奇怪的做法。
通过打表发现这是一个分形,然后可以发现 \(a_{i, j} = (i - 1) \oplus (j - 1) + 1\) (蒟蒻也不知道怎么证明)
考虑把这个矩阵转化为前缀和。现在我们要求的是 \(f(x, y) = \sum\limits_{i = 0}^{x-1} \sum\limits_{j = 0}^{y-1} [i \oplus j + 1 \le t] i \oplus j + 1\)
这个东西可以用类似于二维树状数组的东西来搞。把这个前缀和分成很多个矩阵,每个矩阵都是 \((x - lowbit(x), x), (y - lowbit(y), y)\) 的形式。
我们让 \(lowbit(x) \ge lowbit(y)\)
发现对于每一个区间内的 \(y\) 坐标,那么他们异或上 \(x\) 坐标区间后的值的集合是固定的!让 \(t = (x - lowbit(x) ) \oplus (y - lowbit(y))\),那么 \(y\) 坐标异或上 \(x\) 坐标区间后的值是一段区间,区间都是 \([t, t + lowbit(x))\) (注意之后还要 \(+1\))
时间复杂度 \(\Theta(T \log r_1 \log r_2)\)
代码很好写:
#include<bits/stdc++.h>
#define L(i, j, k) for(int i = j, i##E = k; i <= i##E; i++)
#define R(i, j, k) for(int i = j, i##E = k; i >= i##E; i--)
#define ll long long
#define ull unsigned long long
#define db double
#define pii pair<int, int>
#define mkp make_pair
using namespace std;
const int N = 1e3 + 7;
const int mod = 1e9 + 7;
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar(); }
while('0' <= ch && ch <= '9') x = x * 10 + (ch ^ '0'), ch = getchar();
return x * f;
}
int n, m, T;
int Sum(int l, int r) {
return (ll) (r - l + 1) * (l + r) / 2 % mod;
}
int Get(int l1, int r1, int l2, int r2, int k) {
if(r1 - l1 < r2 - l2) swap(l1, l2), swap(r1, r2);
int mn = (l1 ^ l2), len = r1 - l1;
mn ^= (mn & (len - 1));
if(k <= mn) return 0;
return (ll) (r2 - l2) * Sum(mn + 1, min(k, mn + len)) % mod;
}
int work(int x, int y, int k) {
int res = 0;
for(int i = x; i; i -= (i & -i)) for(int j = y; j; j -= (j & -j)) (res += Get(i - (i & -i), i, j - (j & -j), j, k)) %= mod;
return res;
}
void Main() {
int l1 = read(), l2 = read(), r1 = read(), r2 = read(), k = read();
printf("%d\n", ((work(r1, r2, k) - work(l1 - 1, r2, k) - work(r1, l2 - 1, k)
+ work(l1 - 1, l2 - 1, k)) % mod + mod) % mod);
}
int main() {
T = read();
while(T--) Main();
return 0;
}