题解 P5064【[Ynoi2014] 等这场战争结束之后】
题意
给一张 $n$ 个结点的空图,每个结点有点权 $a_i$,支持操作共 $m$ 次:
- 连边
- 将图恢复到某次操作后的状态
- 查询连通块 $k$ 小
数据范围:$n,m\le 10^5$.
思路
这个题比较的简单。
建出操作树,每一条边代表一次操作,一个结点代表一个状态。
连通块 $k$ 小有许多做法,但我们需要支持快速合并、回撤,所以我们选择值域分块。
先把序列离散化成 $[1,n]$ 的排列。考虑合并值域分块:整块可以直接合并,散块却不好处理。
我们思考一下,发现散块的作用是查询连通块内有没有数 $x$,这个并查集也能做到,并且并查集容易撤销。
撤销时整块可以直接减掉,并查集可以使用可撤销并查集。
令块长为 $T=\Theta(\sqrt{\dfrac n{\log n}})$,时间复杂度为 $\Theta(\dfrac{nm}T+mT\log n)=\Theta(m\sqrt{n\log n})$。
但是,空间复杂度为 $\Theta(nT)=\Theta(\dfrac{n\sqrt n}{\sqrt{\log n}})$,无法达到题目要求的空间。
首先,$T<32768$,所以整块可以用 short 类型存。
然后试着调块长,我调到 $T=4200$ 的时候时空都压线过了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff
}
const int N=1e5+10,sq=4200;
int n,m,bl[N],br[N],bel[N];
int a[N],rev[N],pr[N],op[N],ans[N];
short bnt,blo[N][N/sq+10];
int head[N],cnt,nxt[N];
struct Edge{
int to,nxt;
}s[N];
inline void add(int u,int v){
cnt++;
s[cnt].to=v;
s[cnt].nxt=head[u];
head[u]=cnt;
}
pair<int,int>b[N];
struct node{
int x,y;
}as[N];
namespace ds{
int f[N],siz[N];
inline void init(int sz){
for(int i=1;i<=sz;i++)
f[i]=i,siz[i]=1;
}
inline int find(int x){
for(;x!=f[x];x=f[x]);
return x;
}
inline void swap(int &a,int &b){
int c;
if(siz[a]>siz[b]) c=a,a=b,b=c;
}
inline void merge(int x,int y){
f[x]=y,siz[y]+=siz[x];
}
inline void split(int x,int y){
siz[y]-=siz[x],f[x]=x;
}
}
inline void dfs(int x){
int fx,fy;
if(op[x]==1){
int tx=as[x].x,ty=as[x].y;
fx=ds::find(tx),fy=ds::find(ty);
if(fx==fy) goto out;
ds::swap(fx,fy);
for(int i=1;i<=bnt;i++)
blo[fy][i]+=blo[fx][i];
ds::f[fx]=fy,ds::siz[fy]+=ds::siz[fx];
}
if(op[x]==3){
int tx=as[x].x,k=as[x].y,tmp=-1;
tx=ds::find(tx);
for(int p=1;p<=bnt;p++)
if(blo[tx][p]<k) k-=blo[tx][p];
else{ tmp=p;break; }
if(tmp==-1){ ans[x]=-1;goto out; }
for(int i=bl[tmp];;i++)
if(ds::find(pr[i])==tx)
if(k>1) k--;
else{ ans[x]=rev[i];break; }
}
out:;
for(int i=head[x];i;i=s[i].nxt) dfs(s[i].to);
if(op[x]==1&&fx!=fy){
for(int i=1;i<=bnt;i++)
blo[fy][i]-=blo[fx][i];
ds::siz[fy]-=ds::siz[fx],ds::f[fx]=fx;
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++)
b[i]=make_pair(a[i]=read(),i);
sort(b+1,b+n+1);
for(int i=1;i<=n;i++){
int t=a[i];
a[i]=lower_bound(b+1,b+n+1,make_pair(a[i],i))-b;
pr[a[i]]=i,rev[a[i]]=t;
}
bnt=0;
for(int i=1;i<=n;i+=sq)
bl[++bnt]=i,br[bnt]=min(i+sq-1,n);
for(int i=1;i<=bnt;i++)
for(int j=bl[i];j<=br[i];j++)
bel[j]=i;
ds::init(n);
for(int i=1;i<=n;i++)
blo[i][bel[a[i]]]=1;
for(int i=1;i<=m;i++){
op[i]=read();
if(op[i]!=2) as[i]={read(),read()},add(i-1,i);
else add(read(),i);
}
dfs(0);
for(int i=1;i<=m;i++)
if(op[i]==3) printf("%d\n",ans[i]);
}
再见 qwq~

浙公网安备 33010602011771号