题解:P5386 [Cnoi2019] 数字游戏
nksunhaolan · · 题解
感谢@ljy05提供
正文
step1
对于每一个询问
那么就有
时间
step2
也就是 @ljy05 提出的思路。
一看区间查询、不修改、不强制在线,简单莫队启动!因为是双区间,我们要考虑莫队哪一个区间,但是我们发现如果莫队数组区间,无论是修改还是查询都不好搞的样子,于是考虑莫队值域区间。
当你开始莫队值域区间时,你就会发现豁然开朗。
因为
那现在问题转化为,如何快速单点修改,区间查询。
很容易想到线段树,于是就有了一个
时间
step3
学过莫队的都知道
因为莫队的修改次数为
如何维护呢?我这里提出一种只加入不删除的做法(我太懒了,直接把回滚套上去就不想想删除了)。对于每一个下标维护
修改时看是否会与其他区间合并,如果可以就更新新区间的
统计答案时散块整块查就是了,注意块与块间
时间
代码
#include<bits/stdc++.h>
#define pl pair<int,int>
#define ll long long
using namespace std;
const int N=2e5+5,M=450;
//k,L,R,vis
int n,m,s,t,k,A[N],id[N];
int L[N],R[N];
ll ans[N];
bool vis[N];
struct nnd {
int l,lk,r,x,y,idx;
}q[N];
//ls,rs,ans
struct node {
int l,ls,r,rs,len;
ll ans;
}D[M];
struct mmb {
int x,it,lb,rb,L,R,ls,rs;
ll ans;
}c[N<<1];
inline bool pai(const nnd& x,const nnd& y){
return (x.lk^y.lk)?(x.lk<y.lk):(x.r<y.r);
}
inline void change(const int& x,const bool& dis){
vis[x]=1;
const int it=id[x],l=D[it].l,r=D[it].r;
const int lb=((x^l)&&vis[x-1])?(L[x-1]):(x);
const int rb=((x^r)&&vis[x+1])?(R[x+1]):(x);
if(dis)c[++k]={x,it,lb,rb,L[rb],R[lb],D[it].ls,D[it].rs,D[it].ans};
D[it].ans-=(ll)(x-lb)*(x-lb+1)/2+(ll)(rb-x)*(rb-x+1)/2;
R[lb]=rb,L[rb]=lb;
D[it].ans+=(rb-lb+1)*(rb-lb+2)/2;
if(lb==l)D[it].ls=rb-lb+1;
if(rb==r)D[it].rs=rb-lb+1;
}
void cle(){
k=0;
for(int i=1;i<=t;i++)
D[i].ans=D[i].ls=D[i].rs=0;
for(int i=1;i<=n;i++)
L[i]=R[i]=vis[i]=0;
}
void ret(){
for(;k;k--){
const mmb p=c[k];
vis[p.x]=0;
L[p.rb]=p.L;
R[p.lb]=p.R;
D[p.it].ls=p.ls;
D[p.it].rs=p.rs;
D[p.it].ans=p.ans;
}
}
inline ll get_ans(const int& l,const int& r){
const int st=id[l],ed=id[r];
ll ans=0,sum=0;
if(st==ed){
for(int j=l;j<=r;j++){
if(vis[j]){
sum++;
if(j==r||!vis[j+1])ans+=sum*(sum+1)/2,sum=0;
}
}
} else {
for(int j=l;j<=D[st].r;j++){
if(vis[j]){
sum++;
if(!vis[j+1])ans+=sum*(sum+1)/2,sum=0;
}
}
for(int j=st+1;j<ed;j++){
const ll l=D[j].ls,r=D[j].rs;
if(D[j].len==l)
sum+=l;
else {
sum+=l;
ans+=sum*(sum+1)/2;
sum=r;
ans+=D[j].ans-l*(l+1)/2-r*(r+1)/2;
}
}
if(!vis[D[ed].l]){
ans+=sum*(sum+1)/2;
sum=0;
}
for(int j=D[ed].l;j<=r;j++){
if(vis[j]){
sum++;
if(j==r||!vis[j+1])ans+=sum*(sum+1)/2,sum=0;
}
}
}
return ans;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
s=sqrt(n),t=(n-1)/s+1;
for(int i=1,x;i<=n;i++){
cin>>x;
A[x]=i,id[i]=(i-1)/s+1;
}
for(int i=1,l,r;i<=t;i++){
l=(i-1)*s+1,r=min(i*s,n);
D[i]=(node){l,0,r,0,r-l+1,0};
}
for(int i=1,l,r,x,y;i<=m;i++){
cin>>x>>y>>l>>r;
q[i]=(nnd){l,id[l],r,x,y,i};
}
sort(q+1,q+m+1,pai);
for(int i=1,r,R;i<=m;i++){
r=q[i].lk*s;
if(q[i].lk^q[i-1].lk)cle(),R=r;
for(;R<q[i].r;)change(A[++R],0);
for(int j=min(q[i].r,r);j>=q[i].l;)
change(A[j--],1);
ans[q[i].idx]=get_ans(q[i].x,q[i].y);
ret();
}
for(int i=1;i<=m;i++)
cout<<ans[i]<<"\n";
return 0;
}
码风超抽估计也没入看得懂。