P4119 题解
毒瘤的第一分块,反正我调了整整一周(是我太逊了)。
我的黑历史
这题的操作非常明显:并查集或值域分块(至于为什么不是权值线段树:看看出题人的名字)。
先看第一个操作:看上去只需要一个序列分块套值域分块,但是你发现不能只搞值域,原数组也要修改,但已经有 因为我是做完第二分块再来做这题的),散块拆散重构,整块修改并查集和值域分块。
再看第二个操作:区间 kth,看起来像主席树虽然我不会。但是我们既然选择了分块就要坚持到底。显然是值域上的 好像没有黑科技可以做到 ,所以这里需要对整块的值域做一个前缀和。
因为散块的询问是直接暴力统计的,所以块内不用前缀和,这样的背景下修改也只修改整块的值域,也就是
既然是大分块,那少不了卡常。
- inline
register整起来。 - 块长得开小,因为
O(nB) 的空间需要塞进512MB 里(这样也可以迎合出题人的代码)。 - 细节能压缩就压缩。
- 能遍历初始化的别用 memset。e.g:查询是散块的预处理······
Code
#include <bits/stdc++.h>
using namespace std;
void qr(int &x){
int f=x=0;
char c=getchar();
while(!isdigit(c)) f|=(c=='-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
x=f?~(x-1):x;
}
const int N=100005,Sn=205;
int rt[Sn][N],fa[N],a[N],cnt[N],ccnt[Sn],n,m,s,bls,L[Sn],R[Sn],Ls[Sn],Rs[Sn],blss,ss,idx[N],idxx[N],Maxa,numc[Sn][N],nums[Sn][Sn],opt,l,r,x,y,temp;
inline void merge(int p,int x,int y){
if(x==y) return ;
if(rt[p][x]==0) return;
if(rt[p][y]) fa[rt[p][x]]=rt[p][y];
else a[rt[p][x]]=y,rt[p][y]=rt[p][x];
rt[p][x]=0;
}
int get(int x){
return fa[x]==x?x:fa[x]=get(fa[x]);
}
inline void update(int bl,int l,int r,int x,int y){
rt[bl][x]=rt[bl][y]=temp=0;
for(int i=L[bl];i<=R[bl];i++) {
a[i]=a[get(i)];
}
for(int i=l;i<=r;i++){
if(a[i]==x) a[i]=y,++temp;
}
for(int i=L[bl];i<=R[bl];i++){
if(!rt[bl][a[i]]) rt[bl][a[i]]=i;
fa[i]=rt[bl][a[i]];
}
for(int i=bl;i<=bls;i++) numc[i][x]-=temp,numc[i][y]+=temp,nums[i][idxx[x]]-=temp,nums[i][idxx[y]]+=temp;
return ;
}
int main() {
qr(n),qr(m);
s=600,bls=(n+s-1)/s;
for(int i=1;i<=bls;i++){
L[i]=R[i-1]+1,R[i]=min(s*i,n);
for(int j=L[i];j<=R[i];j++) idx[j]=i;
}
Maxa=1e5;
for(int i=1;i<=n;i++){
qr(a[i]);
}
ss=600,blss=(1e5+ss-1)/ss;
for(int i=1;i<=blss;i++){
Ls[i]=Rs[i-1]+1,Rs[i]=min(ss*i,100000);
for(int j=Ls[i];j<=Rs[i];j++) idxx[j]=i;
}
for(int i=1;i<=bls;i++){
for(int j=L[i];j<=R[i];j++){
if(rt[i][a[j]]==0) rt[i][a[j]]=j;
fa[j]=rt[i][a[j]];
for(int k=i;k<=bls;k++) ++numc[k][a[j]],++nums[k][idxx[a[j]]];
}
}
for(int zqw=1;zqw<=m;zqw++){
qr(opt);
if(opt==1){
qr(l),qr(r),qr(x),qr(y);
int lb=idx[l],rb=idx[r];
if(lb==rb){
update(lb,l,r,x,y);
continue;
}
++lb,--rb;
temp=numc[rb][x]-numc[lb-1][x];
for(int i=lb;i<=rb;i++) {
merge(i,x,y);
int tmp=numc[i][x]-numc[lb-1][x];
nums[i][idxx[y]]+=tmp;
nums[i][idxx[x]]-=tmp;
numc[i][y]+=tmp;
numc[i][x]-=tmp;
}
for(int i=rb+1;i<=bls;i++) numc[i][x]-=temp,nums[i][idxx[x]]-=temp,numc[i][y]+=temp,nums[i][idxx[y]]+=temp;
++rb,--lb;
update(lb,l,R[lb],x,y);
update(rb,L[rb],r,x,y);
}else{
qr(l),qr(r),qr(x);
int lb=idx[l],rb=idx[r];
if(lb==rb){
for(int i=l;i<=r;i++) a[i]=a[get(i)],cnt[a[i]]++,ccnt[idxx[a[i]]]++;
for(int i=1;i<=blss;i++) {
if(x>ccnt[i]) x-=ccnt[i];
else {
for(int j=Ls[i];j<=Rs[i];j++){
if(x>cnt[j]) x-=cnt[j];
else {
printf("%d\n",j);
break;
}
}
break;
}
}
for(int i=l;i<=r;i++) cnt[a[i]]=ccnt[idxx[a[i]]]=0;
continue;
}
++lb,--rb;
for(int i=l;i<L[lb];i++) a[i]=a[get(i)],++cnt[a[i]],++ccnt[idxx[a[i]]];
for(int i=R[rb]+1;i<=r;i++) a[i]=a[get(i)],++cnt[a[i]],++ccnt[idxx[a[i]]];
for(int i=1;i<=blss;i++) {
if(x>nums[rb][i]-nums[lb-1][i]+ccnt[i]) x-=nums[rb][i]-nums[lb-1][i]+ccnt[i];
else {
for(int j=Ls[i];j<=Rs[i];j++){
if(x>numc[rb][j]-numc[lb-1][j]+cnt[j]) x-=numc[rb][j]-numc[lb-1][j]+cnt[j];
else {
printf("%d\n",j);
break;
}
}
break;
}
}
for(int i=l;i<L[lb];i++) cnt[a[i]]=ccnt[idxx[a[i]]]=0;
for(int i=R[rb]+1;i<=r;i++) cnt[a[i]]=ccnt[idxx[a[i]]]=0;
}
}
return 0;
}