CF1172F Nauuo and Bug 题解
事实上一个区间的答案就是区间和减去 乘以减去的次数。
于是问题难点在于如何求一个区间减去的次数。
注意到 ,猜测这可能不是根号科技。考虑朴素线段树。容易观察到的一点是,对于每个区间,假设进去时的初始值为 。然后依次加上区间每个数并取模的时候, 上升的同时,减去 的次数应该是单调不降的。此外我们还发现对于区间 ,减去的次数 。
我们可以对于线段树每个区间,维护 表示想让区间减去次数 ,至少进入多少。首先必然有 单调不降。
如果我们知道了所有区间的每个 ,考虑如何求答案?这是很容易的。对于线段树每个极大区间,从左往右依次进入并二分,把得到的值作为下个区间开始值不断维护,复杂度单次 。
问题是怎么求每个区间的 。一个非常直接的想法是考虑左儿子的区间减 次,右儿子区间减 次。然而前提是 。即左儿子区间减 次能达到最大结果在右儿子区间能减 次。如果满足这个限制,那么 对 的贡献为 。即这个数要在左边能减 次,且在左边结果要在右边能减 次。
直接合并复杂度是两个儿子区间长度乘积,显然不太对,考虑观察性质。
性质 是,总有 成立。因为我想让后面能减少多一次 ,那之前就应该多加一个 。
有这个性质能干什么?考虑从小到大枚举上问的 。考虑那个限制是 。考虑 ,左边容易发现是不降的。所以对于每个 ,符合条件的 是一段前缀。于是可以双指针求出每个 对应所有 。
但是尽管这样,对于每个 ,还是要枚举前缀更新,复杂度还是不对。
考虑对答案的贡献这个式子:。事实上,可以得到 的贡献一定 的贡献。具体地可以通过 的限制来说明。于是每次 只需要更新那些新的 即可。
于是我们做到了 的复杂度。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
using namespace std;
#define int long long
const int N = 1e6 + 5;
int n, m, p, a[N];
class SegmentTree
{
public:
struct Node
{
int l, r;
vector<long long> v;
long long sum;
}tr[N << 2];
void pushup(int u)
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
int l = tr[u].l, r = tr[u].r, mid = l + r >> 1;
int y = 0;
for (int x = 0; x <= mid - l + 1; x++)
{
if (y == r - mid + 1) y--;
if (y < 0) y = 0;
for (; y <= r - mid; y++)
{
long long g = tr[u << 1].v[x + 1] - 1 + tr[u << 1].sum - p * x;
long long k = tr[u << 1 | 1].v[y];
if (g < k)
{
y--;
break;
}
tr[u].v[x + y] = min(tr[u].v[x + y], max(tr[u << 1].v[x], tr[u << 1 | 1].v[y] - tr[u << 1].sum + x * p));
}
}
}
void build(int u, int l, int r)
{
tr[u] = { l, r };
tr[u].sum = a[r];
tr[u].v.resize(r - l + 3);
for (int i = 0; i < r - l + 3; i++) tr[u].v[i] = (long long)4e18;
tr[u].v[0] = (long long)-4e18;
if (l == r)
{
tr[u].v[1] = p - a[r];
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
long long query(int u, int l, int r, long long x)
{
if (tr[u].l >= l and tr[u].r <= r)
{
auto it = upper_bound(tr[u].v.begin() + 1, tr[u].v.end(), x);
--it;
long long g = tr[u].sum + x - (it - tr[u].v.begin()) * p;
return g;
}
int mid = tr[u].l + tr[u].r >> 1;
if (r <= mid) return query(u << 1, l, r, x);
if (l > mid) return query(u << 1 | 1, l, r, x);
return query(u << 1 | 1, l, r, query(u << 1, l, r, x));
}
}sgt;
signed main()
{
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m >> p;
for (int i = 1; i <= n; i++) cin >> a[i];
sgt.build(1, 1, n);
while (m--)
{
int l, r;
cin >> l >> r;
cout << sgt.query(1, l, r, 0ll) << "\n";
}
return 0;
}

浙公网安备 33010602011771号