zoukankan      html  css  js  c++  java
  • 【洛谷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 安装 TortoiseSVN svn 客户端
    linux 定时任务 日志记录
    centos6.5 安装PHP7.0支持nginx
    linux root 用户 定时任务添加
    composer 一些使用说明
    laravel cookie写入
    laravel composer 安装指定版本以及基本的配置
    mysql 删除重复记录语句
    linux php redis 扩展安装
    linux php 安装 memcache 扩展
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5064.html
Copyright © 2011-2022 走看看