[Ynoi2018] 未来日记
老早之前就想写了,人生中第一道大分块,调了一上午+下午一个小时,对拍了不知道多少万组,终于过了。
solution
由于是Ynoi,考虑分块。
数据范围\(10^5\),加上这么诡异的操作,先想分块。
区间rank让人以为是[Ynoi2017] 由乃打扑克这道题,但是如果按照由乃打扑克的思路这题的操作就做不了了。
分块是有自己复杂度的就是\(O(\sqrt{n})\)而不是\(O(\log n)\)这意味着分块其实和log的数据结构以及二分法并不是很搭(因为分块的结构本质上就不支持二分)如果我们需要强行嵌入\(log\)的数据结构的话在绝大部分情况下都会使复杂度凭空多出个\(log\)来,这在强调常数的根号算法中绝对是致命的。——来自shadowice1984的题解。
所以可以考虑时间复杂度同为\(O(\sqrt{n})\)的块状数组,由于\(V\le 10^5\),考虑对值域分块,具体的,就是记\(s1_{i,j}\)表示前\(i\)个序列块中有多少个数在\(j\)值域块中,\(s2_{i,j}\)表示前\(i\)块中有多少个\(j\),查询时先遍历值域块,确定答案所在值域块的位置,然后再遍历值域块找到答案。
简单的查询就处理完了,那么这个逆天的修改如何处理,有一个经典套路就是用并查集来维护,具体的,记\(rt_{i,x}\)表示第\(i\)块中值为\(x\)的数的代表元,这里我用的是每块中第\(1\)个数的位置。然后用并查集维护位置就可以了,整块就直接令\(rt_{i,y}=\min\{rt_{i,y},rt_{i,x}\}\)即可,考虑散块,仍然考虑暴力修,但是如果每个都修爆炸,只需要重构\(rt_{i,x},rt_{i,y}\)为根的子树就行。
时间复杂度\(O((n+m)\sqrt{n})\),实现优秀,cin/cout关了同步流都能过!!!
挂一个你谷第2优解。
点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t; i += p)
#define drep(i,s,t,p) for(int i = s;i >= t; i -= p)
#ifdef LOCALFILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);FILE *ErrFile = freopen("err.err","w",stderr);
#elseFILE *InFile = stdin,*OutFile = stdout;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
namespace IO{const int size = 1<<24;struct IO{char buf[size],*p1,*p2,obuf[size],*op = obuf;#define gc() ((p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2))?EOF:*p1++)inline void pc(char x){if(__builtin_expect(op - obuf == size,1)) fwrite(obuf,1,size,stdout);*op++ = x;}~IO(){fwrite(obuf,1,op-obuf,stdout);}template<class T>inline void read(T &x){x = 0;char s = gc();for(;s < '0' || '9' < s;s = gc());for(;'0' <= s && s <= '9';s = gc())x = (x<<1)+(x<<3)+(s^48);}template<class T,class... Args>inline void read(T &x,Args&... argc){read(x);read(argc...);}template<class T>inline void write(T x){static int sta[30],top = 0;do sta[++top] = x%10;while(x /= 10);while(top) pc(sta[top--]+'0');}inline void write(char x){pc(x);}template<class T,class... Args>inline void write(T x,Args... argc){write(x);write(argc...);}}io;#define read io.read#define write io.write
}using IO::io;
/*
诶不是,我要处理啥啊。
对于值域块处理1~i中有多少个,然后还要处理每个数。
*/
const int N = 1e5 + 1,SN = N/310,V = 1e5 + 1,SV = N/310,RV = 1e5,cjs = 123;
int n,m,a[N];
int L1[SN],R1[SN],p1[N],lens,sizs;
int L2[SV],R2[SV],p2[V],lenv,sizv;
int s1[SN][SV];//前i块中有多少个数在j块中
int s2[SN][V];//前i块中有多少个j
int s3[SN][V];//第i块中有多少个j
int rt[SN][V],fa[N];
int ct1[SV],ct2[V];//查询时用来记录散块
inline int get_fa(int x){while(x^fa[x]) x = fa[x] = fa[fa[x]];return x;}
inline void Merge(int x,int y){if(x > y) swap(x,y);fa[y] = x;}
//将y并到x中,顺序有关。
inline void upd(int l,int r,int x,int y){int p = p1[l],q = p1[r],px = p2[x],py = p2[y],sum = 0;auto rbuild = [&](int p,int l,int r){if(!s3[p][x]) return 0;int res = 0,ff = rt[p][x];vector<int> sonx,sony;rep(i,L1[p],R1[p],1){if(get_fa(i) == rt[p][x]){if(i < l || i > r) sonx.emplace_back(i);else sony.emplace_back(i),++res;}if(fa[i] == rt[p][y]) sony.emplace_back(i);}rt[p][x] = rt[p][y] = 0;for(auto i:sonx){if(!rt[p][x]) rt[p][x] = fa[i] = i;else fa[i] = rt[p][x];a[i] = x;}for(auto i:sony){if(!rt[p][y]) rt[p][y] = fa[i] = i;else fa[i] = rt[p][y];a[i] = y;}s3[p][x] -= res,s3[p][y] += res;sum += res;return res;};if(p == q){int res = rbuild(p,l,r);if(!res) return;rep(i,p,sizs,1){s1[i][px] -= res;s1[i][py] += res;s2[i][x] -= res,s2[i][y] += res;}return;}rbuild(p,l,R1[p]);s1[p][px] -= sum;s1[p][py] += sum;s2[p][x] -= sum,s2[p][y] += sum;rep(i,p+1,q-1,1){if(s3[i][x]){if(!rt[i][y]) rt[i][y] = rt[i][x],a[rt[i][x]] = y;else Merge(rt[i][y],rt[i][x]);fa[rt[i][y] = get_fa(rt[i][y])] = rt[i][y];rt[i][x] = 0;a[get_fa(rt[i][y])] = y;sum += s3[i][x],s3[i][y] += s3[i][x],s3[i][x] = 0;}s1[i][px] -= sum,s1[i][py] += sum;s2[i][x] -= sum,s2[i][y] += sum;}rbuild(q,L1[q],r);s1[q][px] -= sum;s1[q][py] += sum;s2[q][x] -= sum,s2[q][y] += sum;if(!sum) return;rep(i,q+1,sizs,1){s1[i][px] -= sum;s1[i][py] += sum;s2[i][x] -= sum,s2[i][y] += sum;}
}
inline int qry(int l,int r,int k){int p = p1[l],q = p1[r];if(p == q){vector<int> res;rep(i,l,r,1) res.emplace_back(a[get_fa(i)]);nth_element(res.begin(),res.begin()+k-1,res.end());return *(res.begin()+k-1);}rep(i,l,R1[p],1) ++ct2[a[get_fa(i)]],++ct1[p2[a[get_fa(i)]]];rep(i,L1[q],r,1) ++ct2[a[get_fa(i)]],++ct1[p2[a[get_fa(i)]]];int sum = 0,pos = 0,ans = 0;rep(i,1,sizv,1){sum += ct1[i] + s1[q-1][i] - s1[p][i];if(sum >= k){sum -= ct1[i] + s1[q-1][i] - s1[p][i];pos = i;break;}}rep(j,L2[pos],R2[pos],1){if(sum + ct2[j] + s2[q-1][j] - s2[p][j] >= k){ans = j;break;}sum += ct2[j] + s2[q-1][j] - s2[p][j];}rep(i,l,R1[p],1) ct2[a[get_fa(i)]]--,ct1[p2[a[get_fa(i)]]]--;rep(i,L1[q],r,1) ct2[a[get_fa(i)]]--,ct1[p2[a[get_fa(i)]]]--;return ans;
}
inline void solve(){read(n,m);rep(i,1,n,1) read(a[i]),fa[i] = i;lens = 500,sizs = n/lens;rep(i,1,sizs,1) L1[i] = R1[i-1]+1,R1[i] = i*lens;if(R1[sizs] < n) sizs++,L1[sizs] = R1[sizs-1]+1,R1[sizs] = n;lenv = sqrt(RV),sizv = RV/lenv;rep(i,1,sizv,1) L2[i] = R2[i-1]+1,R2[i] = i*lenv;if(R2[sizv] < RV) sizv++,L2[sizv] = R2[sizv-1]+1,R2[sizv] = RV;rep(i,1,sizv,1) rep(j,L2[i],R2[i],1) p2[j] = i;rep(i,1,sizs,1){rep(j,L1[i],R1[i],1){p1[j] = i;s1[i][p2[a[j]]]++;s2[i][a[j]]++;s3[i][a[j]]++;if(!rt[i][a[j]]) rt[i][a[j]] = j;else fa[j] = rt[i][a[j]];}rep(j,1,sizv,1) s1[i][j] += s1[i-1][j];rep(j,1,RV,1) s2[i][j] += s2[i-1][j];}rep(test,1,m,1){int op,l,r,x,y;read(op);if(op ^ 2) read(l,r,x,y),(x^y)?upd(l,r,x,y):void();else read(l,r,x),write(qry(l,r,x),'\n');}
}
signed main(){solve();}