AT_abc340_e 题解
思路
考虑使用支持区间加和单点查询的线段树维护。
对于每一个操作,先查询当前盒子内的球有多少个,设有
然后先看能加几圈,也就是
把这些圈加上后,看剩下部分加的区间是什么即可。
Code
#include <iostream>
using namespace std;
const int N = 200005;
typedef long long LL;
LL w[N << 2], tag[N << 2];
int n, m;
inline void pushup(int u)
{
w[u] = w[u << 1] + w[u << 1 | 1];
}
void build(int u,int l,int r)
{
if (l == r)
{
scanf("%lld", &w[u]);
return;
}
int mid = (l + r) >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void maketag(int u,int l,int r,LL k)
{
tag[u] += k;
w[u] += k * (r - l + 1);
}
void pushdown(int u,int l,int r,int mid)
{
maketag(u << 1, l, mid, tag[u]);
maketag(u << 1 | 1, mid + 1, r, tag[u]);
tag[u] = 0;
}
void update(int u,int l,int r,int L,int R,int x)
{
if (L <= l && r <= R)
{
maketag(u, l, r, x);
return;
}
int mid = (l + r) >> 1;
if (tag[u])
pushdown(u, l, r, mid);
if (L <= mid)
update(u << 1, l, mid, L, R, x);
if (mid < R)
update(u << 1 | 1, mid + 1, r, L, R, x);
pushup(u);
}
LL query(int u,int l,int r,int x)
{
if (l == r)
return w[u];
int mid = (l + r) >> 1;
if (tag[u])
pushdown(u, l, r, mid);
if (x <= mid)
return query(u << 1, l, mid, x);
return query(u << 1 | 1, mid + 1, r, x);
}
void update1(int u,int l,int r,int x)
{
if (l == r)
{
w[u] = 0;
return;
}
int mid = (l + r) >> 1;
if (tag[u])
pushdown(u, l, r, mid);
if (x <= mid)
update1(u << 1, l, mid, x);
else
update1(u << 1 | 1, mid + 1, r, x);
pushup(u);
}
void change(int l,int r)
{
if (l > n)
{
l -= n;
r -= n;
}
if (r <= n)
update(1, 1, n, l, r, 1);
else
{
update(1, 1, n, l, n, 1);
update(1, 1, n, 1, r - n, 1);
}
}
int main()
{
scanf("%d%d", &n, &m);
build(1, 1, n);
while (m--)
{
int x;
scanf("%d", &x);
x ++;
LL ai = query(1, 1, n, x);
update1(1, 1, n, x);
if (ai >= n)
update(1, 1, n, 1, n, ai / n);
ai %= n;
if (ai)
change(x + 1, x + ai);
}
for (int i=1;i<=n;i++)
printf("%lld ", query(1, 1, n, i));
return 0;
}