Luogu P11660 我终将成为你的倒影 题解 [ 紫 ] [ 分块 ] [ 分类讨论 ]
我终将成为你的倒影:考察分块基本功的一道题。
注意到本题强制在线,且这种信息用线段树不是很好维护,所以可以很自然地想到分块。
又注意到 \(b \le 500\),所以考虑暴力枚举 \(b\)。发现当 \(b\) 固定的时候,\(a\) 取 \(a, a+b, a+2b\) 等数字答案是一样的,因为可以把分子中的 \(b\) 提出来,不会影响前后两个是否相等。于是可以把 \(\bm a\) 转化为 $ \bm{a\bmod b}$ 考虑。
因为将 \(a\) 对 \(b\) 取模了,所以此时 \(a< b \le 500\),容易想到一个暴力做法:将序列分块,每块维护 \(a, b\) 各自取值时的答案,查询的时候暴力查散块,整块直接加,两个块之间暴力计算,即可做到查询 \(O(\frac{n}{B})\),预处理 \(O(nb^2)\)。显然暴力预处理的部分只要把块数调小一点就不会 MLE,所以只需要优化预处理部分的时间复杂度即可。
继续观察这个函数的性质,因为所有数的 \(b\) 都是相等的,所以当两个数的函数值相差大于等于 \(\bm{2}\) 的时候,这两个数的函数值在任何时候都无法相等。因此枚举 \(b\) 之后,只需要考虑函数值相差 \(\bm1\) 的位置,根据 \(a\bmod b\) 的值分讨即可。具体而言,令 \(x = A_{i - 1} \bmod b, y = A_i \bmod b\):
- 当 \(A_{i - 1} = A_i\) 时,\(a\in[0, b)\)。
- 当 \(f(A_{i - 1}) = f(A_i)\) 时,\(a\in[0, b - \max\{x, y\})\cup[b - \min\{x, y\}, b)\)。
- 当 \(f(A_{i - 1}) = f(A_i) + 1, x < y\) 时,\(a\in[b - y, b - x)\)。
- 当 \(f(A_{i - 1})+1 = f(A_i) , x > y\) 时,\(a\in[b - x, b - y)\)。
- 其余情况 \(a\in \varnothing\)。
这样就可以 \(O(1)\) 计算 \(a\) 的区间了,做一个差分计算贡献即可。
总体时间复杂度 \(O(B+\frac{n}{B}+nb+\frac{n}{B}b^2)\)。当 \(\frac{n}{B} = B\) 时最优,因此 \(B = \sqrt n\)。但是因为此题卡空间,所以块长可能要调大一些。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 100005, M = 1005, MV = 105, V = 505;
int n, m, a[N];
struct Decompose{
int B, Btot, L[M], R[M], bl[N];
int val[MV][V][V];
void build()
{
B = sqrt(n);
Btot = (n + B - 1) / B;
for(int i = 1; i <= Btot; i++)
{
L[i] = (i - 1) * B + 1;
R[i] = min(n, i * B);
for(int j = L[i]; j <= R[i]; j++) bl[j] = i;
}
}
void init()
{
for(int b = 1; b <= 500; b++)
{
for(int i = 1; i <= Btot; i++)
{
for(int j = L[i] + 1; j <= R[i]; j++)
{
if(a[j] == a[j - 1])
{
val[i][b][0]++;
continue;
}
if(abs(a[j] / b - a[j - 1] / b) >= 2) continue;
int x = a[j - 1] % b, y = a[j] % b;
if(a[j - 1] / b > a[j] / b)
{
if(y > x)
{
val[i][b][b - y]++;
val[i][b][b - x]--;
}
}
else if(a[j - 1] / b < a[j] / b)
{
if(x > y)
{
val[i][b][b - x]++;
val[i][b][b - y]--;
}
}
else
{
val[i][b][0]++;
val[i][b][b - max(x, y)]--;
val[i][b][b - min(x, y)]++;
}
}
for(int j = 1; j < b; j++) val[i][b][j] += val[i][b][j - 1];
}
}
}
int query(int l, int r, int x, int y)
{
int res = 0;
if(bl[l] == bl[r])
{
for(int i = l + 1; i <= r; i++)
res += ((a[i - 1] + x) / y == (a[i] + x) / y);
return res;
}
for(int i = l + 1; i <= R[bl[l]]; i++)
res += ((a[i - 1] + x) / y == (a[i] + x) / y);
for(int i = L[bl[r]]; i <= r; i++)
res += ((a[i - 1] + x) / y == (a[i] + x) / y);
for(int i = bl[l] + 1; i < bl[r]; i++)
{
res += val[i][y][x % y];
res += ((a[L[i] - 1] + x) / y == (a[L[i]] + x) / y);
}
return res;
}
}dc;
int main()
{
//freopen("sample.in", "r", stdin);
//freopen("sample.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> a[i];
dc.build();
dc.init();
int lastans = 0;
for(int i = 1; i <= m; i++)
{
int l, r, x, y;
cin >> l >> r >> x >> y;
l ^= lastans;
r ^= lastans;
x ^= lastans;
y ^= lastans;
lastans = dc.query(l, r, x, y);
cout << lastans << "\n";
}
return 0;
}