P4117 【[Ynoi2018]五彩斑斓的世界】题解

P4117 【[Ynoi2018]五彩斑斓的世界】

"突刺贯穿的第二分块"

调了一天的第二分块最后发现是 memset 的问题。。。(机房巨佬nb)

大致思路相信第一篇题解已经讲的很清楚了,这里珂以再作阐述:

考虑分块:对于每一个大小为 (sqrt{n}) 的块,

先考虑修改:

设整个块的最大值为 maxn ,修改值为 x 珂以分两种情况处理:

  1. (x*2<=maxn-tag), 对于这个块里的值,如题意一般,将所有(geq x)的数都(-x),修改后的 (maxn) 依旧不变

  2. (x*2>maxn-tag) 对于整个块的操作其实也就等价于将所有(leq x)(+x),再将整个区间打上一个(-x)(tag)

再用一个并查集维护值域,

记录下这个值下的(size),与这个值第一次出现位置(st),还有第一次出现位置所对应的映射 (rev)

操作1也就相当于将(ain(tag+x,maxn])中的每个数都与(a-x)合并

操作2也就相当于先将(a in [tag+1,x+tag])(a+x) 合并,再打上一个 (-x)(tag)

对于不属于整块的操作直接暴力重新构造就好了

查询的时候整块直接查 (x+tag)(size)

不属于的直接暴力求解就好了

然后出题人的题解中,有一个逐块处理的trick

大致就是统计每一个块对于每一个询问的贡献,这样就可以做到(O(N+M))

这个优化的很多前提珂以看这篇题解

还有记住每一次统计的时候记得清空并查集中的信息,但是重构的时候不用清空,(我的惨烈调试记录)

丑陋的代码~~~

#include<bits/stdc++.h>
using namespace std;
inline int read(){
   int x=0,f=1; char ch=getchar();
  while(!isdigit(ch)){if(ch=='-'){f=-1;}ch=getchar();}
  while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}
  return x*f;
}
inline void write( int x){
  if(x==0){putchar(48);putchar('
');return;}int len=0,dg[20];
  while(x>0){dg[++len]=x%10;x/=10;}
  for( int i=len;i>=1;i--) putchar(dg[i]+48);
  putchar('
');
}
const int MAXN=1e6+5,N=1e6+5,SQRTN=1e4+5,M=1e6+5;;
int n,m,L[MAXN],R[MAXN],a[N],ans[M];
int st[MAXN],fa[MAXN],size[MAXN],rev[MAXN],maxn,tag_dec;
int ql[M],qr[M],qx[M],op[M],len;
inline int get(int x){return fa[x]==x ? x : fa[x]=get(fa[x]);}
inline void merge(int x,int y){
  if(st[y]){fa[st[x]]=st[y];}
  else{st[y]=st[x];rev[st[y]]=y;}
  size[y]+=size[x];st[x]=size[x]=0;
}
inline void pre_build(){
  len=sqrt(n);
  for( int i=1;i<=len;++i){L[i]=(i-1)*len+1;R[i]=i*len;}
  if(R[len]<n){len++;L[len]=R[len-1]+1;R[len]=n;}
}
inline void build_each(int num){
  maxn=0,tag_dec=0;
  for( int i=L[num];i<=R[num];++i){
    maxn=max(maxn,a[i]);
    if(st[a[i]]){fa[i]=st[a[i]];}
    else{st[a[i]]=fa[i]=i;rev[i]=a[i];}
    size[a[i]]++;
  }
}
inline void modify(int x){
  if(maxn-tag_dec>=x*2){
    for( int i=tag_dec+1;i<=tag_dec+x;++i){if(st[i]){merge(i,x+i);}}
    tag_dec+=x;
  }
  else{
    for( int i=x+tag_dec+1;i<=maxn;++i){if(st[i]){merge(i,i-x);}}
    maxn=min(maxn,tag_dec+x);
  }
}
inline void rebuild(int num,int queryl,int queryr,int x){
  for( int i=L[num];i<=R[num];++i){
    a[i]=rev[get(i)];
    st[a[i]]=size[a[i]]=0;
    a[i]-=tag_dec;
  }
  for( int i=L[num];i<=R[num];++i){rev[i]=0;}
  int lim=min(queryr,R[num]);
  int liml=max(L[num],queryl);
  for( int i=liml;i<=lim;++i){a[i]=a[i]-(a[i]>x)*x;}
  build_each(num);
}
inline void query(int num,int querynum){
  int x=qx[querynum],queryr=qr[querynum],queryl=ql[querynum];
  if(x+tag_dec>5e5){return;}
  if(queryl<=L[num]&&R[num]<=queryr){ans[querynum]+=size[x+tag_dec];}
  else{
    int lim=min(queryr,R[num]);
    int liml=max(queryl,L[num]);
    for(int i=liml;i<=lim;++i){if(rev[get(i)]==x+tag_dec){ans[querynum]++;}}
  }
}
inline void check(){puts("??????woc?????");}
int main(){
  n=read(),m=read();
  for( int i=1;i<=n;++i){a[i]=read();}
  pre_build();
  for( int i=1;i<=m;++i){op[i]=read(),ql[i]=read(),qr[i]=read(),qx[i]=read();}
  for( int i=1;i<=len;++i){
    memset(st,0,sizeof(st));
    memset(size,0,sizeof(size));
    build_each(i);
    for( int j=1;j<=m;++j){
      if(qr[j]<L[i]||ql[j]>R[i]){continue;}
      if(op[j]==1){
        if(L[i]>=ql[j]&&R[i]<=qr[j]){modify(qx[j]);}
        else{rebuild(i,ql[j],qr[j],qx[j]);}
      }
      else{query(i,j);}
    }
  }
  for(int i=1;i<=m;++i){if(op[i]==2){write(ans[i]);}}
  return 0;
}


原文地址:https://www.cnblogs.com/NuoCarter/p/14448876.html