P5064 [Ynoi2014] 等这场战争结束之后
题意
给定一个图,每个点有点权,最开始没有边。
有一些操作:
- 添加一条 $x$ 与 $y$ 之间的双向边。
- 回到第 $x$ 次操作后的状态(注意这里的 $x$ 可以是 $0$,即回到初始状态)。
- 查询 $x$ 所在联通块能到的点中点权第 $k$ 小的值,如果不存在,那么输出 $-1$。
Solution
发现没有强制在线,考虑将操作离线并建立操作树。对于 $1,3$ 操作,将 $i$ 向 $i-1$ 连一条边即可。对于 $2$ 操作,向 $x$ 连一条边,最后对于操作树 dfs 求出所有答案即可。
在 dfs 时,若对于每个询问暴力查找第 $k$ 小数显然时间复杂度不正确。考虑优化,发现对于点权离散化后值域为 $[1,10^6]$,对值域分块。对于每个连通块而言,维护其在每个值域块内的节点个数,这样查找答案的最坏复杂度为 $\mathcal{O}(\sqrt{n})$。在操作 $1$ 和回溯时暴力合并和分离信息即可,加上可撤销并查集,这样做时间复杂度是 $\mathcal{O}(\sqrt{n}\log n)$ 。
故总时间复杂度为 $\mathcal{O}(n\sqrt{n}\log n )$,空间复杂度为 $\mathcal{O}(n\sqrt{n})$。
但是由于毒瘤 lxl 把空间开到了奇怪的地步,调调块长然后开 short 即可在时间限制和空间限制内通过本题。
实测块长开成 $2500$ 能过,跑的还蛮快的。
code
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int res=0,flag=1;
char ch=getchar();
while(!isalnum(ch)) (ch=='-')?flag=-1:1,ch=getchar();
while(isalnum(ch)) res=res*10+ch-'0',ch=getchar();
return res*flag;
}
inline void write(int x)
{
char buf[20]; int len=0;
if(x<=0) putchar((x==0)?'0':'-'); x=abs(x);
while(x!=0) buf[++len]=x%10+'0',x/=10;
while(len!=0) putchar(buf[len--]);
putchar('\n');
return ;
}
struct edge
{
int to,nxt;
};
struct node
{
short opt;
int x,y;
};
struct edge ed[100010];
struct node nd[100010];
int n,m,tot,len,sum;
int val[100010];
int id[100010],fa[100010];
int head[200010];
int ans[100010];
int size[100010];
unsigned short data[100010][41];
void init()
{
n=read(),m=read();
len=min(n,2500),sum=ceil((double)(n/len));
for(int i=1;i<=n;i++)
val[i]=read(),fa[i]=id[i]=i;
sort(id+1,id+n+1,[](int a,int b)->bool{return val[a]<val[b];});
for(int i=1,cnt=1;i<=n;i++)
{
size[i]=data[id[i]][cnt]=1;
if(i%len==0)
cnt++;
}
return ;
}
void add_edge(int fr,int to)
{
ed[++tot]=(edge){to,head[fr]};
head[fr]=tot;
return ;
}
int find(int x)
{
if(fa[x]==x)
return x;
return find(fa[x]);
}
void add(int &x,int &y)
{
x=find(x),y=find(y);
if(x==y) return ;
if(size[x]<size[y])
swap(x,y);
fa[y]=x;
size[x]+=size[y];
for(int i=1;i<=sum;i++)
data[x][i]+=data[y][i];
return ;
}
void del(int x,int y)
{
if(x==y) return ;
fa[y]=y;
size[x]-=size[y];
for(int i=1;i<=sum;i++)
data[x][i]-=data[y][i];
return ;
}
int query(int pos,int k)
{
pos=find(pos);
if(size[pos]<k)
return -1;
for(int i=1;i<=sum;i++)
{
if(k-data[pos][i]>0)
{
k-=data[pos][i];
continue;
}
for(int j=len*(i-1)+1;j<=len*i+1;j++)
{
if(find(id[j])==pos) k--;
if(k==0) return val[id[j]];
}
}
return -1;
}
void dfs(int fr)
{
if(nd[fr].opt==1)
add(nd[fr].x,nd[fr].y);
if(nd[fr].opt==3)
ans[fr]=query(nd[fr].x,nd[fr].y);
for(int i=head[fr];i!=0;i=ed[i].nxt)
dfs(ed[i].to);
if(nd[fr].opt==1)
del(nd[fr].x,nd[fr].y);
return ;
}
int main(int argc,const char *argv[])
{
init();
for(int i=1;i<=m;i++)
{
nd[i].opt=read();
if(nd[i].opt==2)
{
nd[i].x=read();
add_edge(nd[i].x,i);
continue;
}
nd[i].x=read();
nd[i].y=read();
add_edge(i-1,i);
}
dfs(0);
for(int i=1;i<=m;i++)
if(nd[i].opt==3)
write(ans[i]);
return 0;
}

浙公网安备 33010602011771号