• 【洛谷5064】[Ynoi2014] 等这场战争结束之后(操作树+值域分块)


    点此看题面

    • 给定一张(n)个点的图,初始没有边。
    • 要求支持三种操作:加入一条无向边;回到之前某次操作后的状态;询问一个连通块的第(k)小值。
    • (n,qle10^5)

    操作树上(dfs)

    这类问题的经典操作。

    只要建出操作树,就只需要考虑一次操作的加入与撤销即可。

    值域分块+并查集

    首先这道题必须要离散化,但离散化时我们不应去重,这样就可以保证离散化后的值与原数一一对应(然而好像数据里面并没有重复的数)。

    然后把值域分块,对于每个连通块维护每个块的数的个数。

    询问答案时先找到答案在哪个块,再枚举这个块中的每个值,找到对应点判断是否与询问点连通(可以使用并查集维护)。

    注意这道题卡内存,由于一个值域块内的数不超过(S)个可以开成(short),那么块长(S)可以取到(2500),块个数为(40)

    代码:(O(nsqrt{nlogn}))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define BS 2500
    #define BT 40
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    using namespace std;
    int n,Qt,a[N+5],id[N+5];struct Q {int op,x,y;}q[N+5];
    int ee,lnk[N+5];struct edge {int to,nxt;}e[N+5];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
    	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
    	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('
    ');}
    	I void NA() {pc('-'),pc('1'),pc('
    ');}
    }using namespace FastIO;
    namespace V//值域分块
    {
    	int sz,bl[N+5],p[N+5];short c[N+1][BT+1];
    	I void U(CI x,CI y,CI op) {for(RI i=1;i<=bl[n];++i) c[x][i]+=op*c[y][i];}//给x的数组加上/减去y的数组
    	int dv[N+5],nw[N+5];I void Build()//预处理
    	{
    		RI i,x;for(sz=min(BS,n),i=1;i<=n;++i) bl[i]=(i-1)/sz+1;for(i=1;i<=n;++i) dv[i]=a[i];sort(dv+1,dv+n+1);
    		for(i=1;i<=n;++i) c[i][bl[a[i]=nw[x=lower_bound(dv+1,dv+n+1,a[i])-dv]?++nw[x]:(nw[x]=x)]]=1,p[a[i]]=i;//不去重离散化,并对应到原数
    	}
    }
    namespace U//按秩合并可撤销并查集
    {
    	int f[N+5],g[N+5],T,Sx[N+5],Sy[N+5];I int fa(CI x) {return f[x]?fa(f[x]):x;}
    	I void Init() {for(RI i=1;i<=n;++i) g[i]=1;}//初始赋值
    	I void Union(RI x,RI y)//合并
    	{
    		++T;if((x=fa(x))==(y=fa(y))) return (void)(Sx[T]=-1);//已连通则合并无效
    		g[x]<g[y]&&(swap(x,y),0),g[Sx[T]=f[y]=x]+=g[Sy[T]=y],V::U(x,y,1);
    	}
    	I void Back() {~Sx[T]&&(g[Sx[T]]-=g[Sy[T]],V::U(Sx[T],Sy[T],-1),f[Sy[T]]=0),--T;}//撤销
    }
    I int Q(RI x,RI y)//询问x所在连通块第y大
    {
    	if(U::g[x=U::fa(x)]<y) return -1;RI i;for(i=1;V::c[x][i]<y;++i) y-=V::c[x][i];//找到在哪个块
    	for(i=(i-1)*V::sz+1;;++i) if(U::fa(V::p[i])==x&&!--y) return i;//枚举块中值找到对应点判连通
    }
    int ans[N+5];I void dfs(CI x)//操作树上dfs
    {
    	q[x].op==1&&(U::Union(q[x].x,q[x].y),0),q[x].op==3&&(ans[x]=Q(q[x].x,q[x].y));//处理当前点操作
    	for(RI i=lnk[x];i;i=e[i].nxt) dfs(e[i].to);q[x].op==1&&(U::Back(),0);//遍历子树,撤销操作
    }
    int main()
    {
    	RI i,op,x;for(read(n,Qt),i=1;i<=n;++i) read(a[i]);V::Build();
    	for(i=1;i<=Qt;++i) read(op),op^2?(q[i].op=op,read(q[i].x,q[i].y),add(id[i-1],i),id[i]=i):(read(x),id[i]=id[x]);//id[i]记录第i个操作后对应节点
    	for(U::Init(),dfs(0),i=1;i<=Qt;++i) q[i].op==3&&(~ans[i]?writeln(V::dv[ans[i]]):NA(),0);return clear(),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    CentOS linux系统搭建LAMP环境
    网站跳出率高的优化方案
    IT痴汉的工作现状24-Just for fun
    windows下远程连接ubantu
    Hibernate基础映射
    我院同学在2013年第四届“蓝桥杯”全国软件专业人才设计与创业大赛全国总决赛中获得佳绩
    Linux下打开串口设置
    zoj 3261 Connections in Galaxy War
    Android之startActivityForResult的使用
    当心Azure跨区域数据传输产生额外费用
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5064.html
Copyright © 2020-2023  润新知