和 数列分块入门 2 差不多的思路,
对每个块排序,然后就可以在上面二分,求和,发现都是在二分出来的位置前面的数,可以用前缀和预处理出来
分块按照一般分法
n, q 同阶,时间复杂度是 \(O(n\sqrt{n}\log\sqrt{n})\)
然后交上去发现最后几个点 T 了,算一下,大概是 2e5 * 450 * 8 = 7e8,时限是 3.5s,可承受的时间应该是 3.5 * 1e8 = 3.5e8 左右
超了一些
注意这里前后两个 \(\sqrt{n}\) 的意义是不同的,前面表示块数,后面的表示平均块长
注意到 \(f(x) = x\) 和 \(g(x) = \log_2x\) 的增长是有差距的,后一个显然增长更慢
那么我们考虑调整块长,让块数少一点,块长大一点,这样前一个的减少量肯定是远大于后一个的增加量的
比如调整块长为 2000,也就是 \(O(n * \frac{n}{2000} * \log(2000))\),2e5 * 100 * 10 = 2e8
所以,分块卡常一般就是调整块数与块长之间的大小(可能吧
#include <bits/stdc++.h>
#define re register int
#define int long long
using namespace std;
const int N = 2e5 + 10;
int n, m, a[N];
int pos[N], L[N], R[N], sum[1010][2010];
vector<int> v[1010];
inline void init()
{
int t = n / 2000 + (n % 2000 ? 1 : 0);
for (re i = 1; i <= t; i ++)
{
L[i] = (i - 1) * 2000 + 1;
R[i] = i * 2000;
}
if (R[t] < n) t ++, L[t] = R[t - 1] + 1, R[t] = n;
for (re i = 1; i <= t; i ++)
{
for (re j = L[i]; j <= R[i]; j ++)
{
pos[j] = i;
v[i].push_back(a[j]);
}
sort(v[i].begin(), v[i].end());
for (re j = 0; j < v[i].size(); j ++)
{
if (j == 0) sum[i][j] = v[i][j];
else sum[i][j] = sum[i][j - 1] + v[i][j];
}
}
// for (re i = 1; i <= n; i ++) cout << pos[i] << ' '; cout << '\n';
}
inline int query(int l, int r, int c)
{
int p = pos[l], q = pos[r];
int res = 0;
if (p == q)
{
for (re i = l; i <= r; i ++)
if (a[i] <= c) res += a[i];
}
else
{
for (re i = l; i <= R[p]; i ++)
if (a[i] <= c) res += a[i];
for (re i = L[q]; i <= r; i ++)
if (a[i] <= c) res += a[i];
for (re i = p + 1; i <= q - 1; i ++)
{
int x = upper_bound(v[i].begin(), v[i].end(), c) - v[i].begin();
if (x - 1 < 0) continue;
res += sum[i][x - 1];
}
}
return res;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n;
for (re i = 1; i <= n; i ++) cin >> a[i];
init();
cin >> m;
int last = 0;
while (m --)
{
int l, r, c; cin >> l >> r >> c; l ^= last, r ^= last, c ^= last;
int ans = query(l, r, c);
cout << ans << '\n';
last = ans;
}
return 0;
}
标签:Smaller,last,分块,int,Sum,2e5,2000,ABC339G,块长
From: https://www.cnblogs.com/wenzieee/p/18547801