【JZOJ 1384】 Alice的游戏
题目大意:
给出一个长度为 \(N\) 的数列,\(M\) 次操作,每次操作两个数 \(a\) 和 \(b\),且在数列中的闭区间 \([a,b]\) 中的每个数都要加一且对其模十,在此之前要求出每个数的和。
正文:
因为 \(N \leq250000\) 所以要考虑 \(O(n\log n)\) 的算法,这里用到线段树。
本题可以用线段树每个节点维护一段零至九的序列就好了,本题码量大,主要考验的是代码实现能力。
代码:
inline void opt(int x) {
sgt[x].val = sgt[x << 1].val + sgt[x << 1 | 1].val;
for(register int i = 0; i <= 9; ++i) {
val[x][i] = val[x << 1][i] + val[x << 1 | 1][i];
}
}
void build(int x, int l, int r) {
sgt[x].l = l, sgt[x].r = r;
if(l == r) {
sgt[x].val = a[l];
++val[x][a[l]];
return ;
}
int mid = (sgt[x].l + sgt[x].r) / 2;
build(x << 1, l, mid);
build(x << 1 | 1, mid + 1, r);
opt(x);
}
int tool[11];
void spreadd(int x)
{
for(int i = 0; i <= 9; i++)
{
tool[(i + sgt[x].dif) % 10] = val[x][i];
}
sgt[x].val = 0;
for(int i = 0; i <= 9; i++)
{
val[x][i] = tool[i];
sgt[x].val += val[x][i] * i;
}
sgt[x].dif = 0;
}
void spread(int x) {
if(sgt[x].lzy) {
sgt[x << 1].lzy += sgt[x].lzy;
sgt[x << 1 | 1].lzy += sgt[x].lzy;
sgt[x << 1].dif += sgt[x].lzy;
sgt[x << 1 | 1].dif += sgt[x].lzy;
sgt[x].lzy = 0;
spreadd(x << 1);
spreadd(x << 1 | 1);
spreadd(x);
}
}
int search(int x, int l, int r) {
if(l <= sgt[x].l && sgt[x].r <= r) {
return sgt[x].val;
}
spread(x);
int mid = (sgt[x].l + sgt[x].r) / 2;
if(mid < l) return search(x << 1 | 1, l, r);
if(mid >= r) return search(x << 1, l, r);
return search(x << 1, l, mid) + search(x << 1 | 1, mid + 1, r);
}
void change(int x, int l, int r)
{
if(l == sgt[x].l && r == sgt[x].r)
{
sgt[x].lzy++;
sgt[x].dif++;
spreadd(x);
return;
}
spread(x);
int mid = (sgt[x].l + sgt[x].r) / 2;
bool flag = 1;
if(mid < l) change(x << 1 | 1, l, r), flag=0;
if(mid >= r) change(x << 1, l, r), flag=0;
if(flag) {
change(x << 1, l, mid);
change(x << 1 | 1, mid + 1, r);
}
opt(x);
}