[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 LOCAL
FILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
FILE *ErrFile = freopen("err.err","w",stderr);
#else
FILE *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();}
本文来自博客园,作者:CuFeO4,转载请注明原文链接:https://www.cnblogs.com/hzoi-Cu/p/18544094