[Ynoi2018] 五彩斑斓的世界
[Ynoi2018] 五彩斑斓的世界
突刺贯穿的第二分块。
题意
给定长度为 \(n\) 的序列 \(a\),有 \(m\) 次操作,每次操作有两种形式:
-
将区间 \([l,r]\) 中大于 \(x\) 的数减去 \(x\)。
-
求区间 \([l,r]\) 中等于 \(x\) 的数的个数。
\(1 \leq n \leq 10^6\),\(1 \leq m \leq 5 \times 10^5\),\(0 \leq a_i,x \leq 10^5+1\)。
思路
看到 Ynoi 题可以直接考虑分块了。
考虑整块的修改操作。假设块中元素上界为 \(mx\),则有:
-
\(mx < 2 \times x\),此时我们对值域在 \([x+1,mx]\) 中的元素减去 \(x\),然后令 \(mx\) 改为 \(x\)。
-
\(mx \geq 2 \times x\),此时我们对值域在 \([0,x]\) 的元素加上 \(x\),再打一个整块减 \(x\) 的标记,然后令 \(mx\) 减去 \(x\)。
虽然 \(mx\) 不是严格的上界,但我们仍然可以证明,单个块产生的修改总数是不超过极差也就是 \(O(V)\) 的。
现在考虑如何快速维护这一信息,发现对每个值开一个并查集即可。
但是我们发现这题卡了空间。块与块之间互不影响,所以我们离线下来逐块处理。
代码
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int V=100001;
int n,m,num[1000010],ans[500010],root[100010],rev[1200010],cnt[1200010],p[1200010];
struct Query{
int cmd,l,r,x;
}query[500010];
int Find(int pos){
if(pos!=p[pos]) p[pos]=Find(p[pos]);
return p[pos];
}
void calc(int L,int R){
int mx=V,tag=0,tot=n;
for(int i=0;i<=V;i++){
tot++;
root[i]=tot;
rev[tot]=i;
p[root[i]]=root[i];
cnt[root[i]]=0;
}
for(int i=L;i<=R;i++){
p[i]=root[num[i]];
cnt[root[num[i]]]++;
}
for(int i=1;i<=m;i++){
int cmd=query[i].cmd,l=max(query[i].l,L),r=min(query[i].r,R),x=query[i].x;
if(l>r){
continue;
}
if(cmd==1 && x){
if(l==L && r==R){
if(mx<=x){
continue;
}
else if(mx<2*x){
for(int j=x+1+tag;j<=mx+tag;j++){
p[root[j]]=root[j-x];
cnt[root[j-x]]+=cnt[root[j]];
cnt[root[j]]=0;
tot++;
root[j]=tot;
rev[tot]=j;
p[root[j]]=root[j];
cnt[root[j]]=0;
}
mx=x;
}
else{
for(int j=x+tag;j>=tag;j--){
p[root[j]]=root[j+x];
cnt[root[j+x]]+=cnt[root[j]];
cnt[root[j]]=0;
tot++;
root[j]=tot;
rev[tot]=j;
p[root[j]]=root[j];
cnt[root[j]]=0;
}
tag+=x;
mx-=x;
}
}
else{
for(int j=l;j<=r;j++){
int num=rev[Find(j)];
if(num-tag>x){
cnt[root[num]]--;
cnt[root[num-x]]++;
p[j]=root[num-x];
}
}
}
}
if(cmd==2){
if(l==L && r==R){
if(x+tag<=V) ans[i]+=cnt[root[x+tag]];
}
else{
for(int j=l;j<=r;j++){
int num=rev[Find(j)]-tag;
if(num==x){
ans[i]++;
}
}
}
}
}
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&num[i]);
}
for(int i=1;i<=m;i++){
scanf("%d %d %d %d",&query[i].cmd,&query[i].l,&query[i].r,&query[i].x);
}
int block=round(sqrt(n));
for(int L=1;L<=n;L++){
int R=min(L+block-1,n);
calc(L,R);
L=R;
}
for(int i=1;i<=m;i++){
if(query[i].cmd==2){
printf("%d\n",ans[i]);
}
}
return 0;
}

浙公网安备 33010602011771号