[LibreOJ β Round #2]数学上来先打表

数学上来先打表

题解

我们很容易发现,这种构成连通块的查询元素的题很容易用带权并查集来处理。

由于它第三个操作求的是块中第k小的数,如果一个一个查询肯定不好处理,我们需要一种更加高效的查询方式。

于是,我们就想到了分块,将每个点的导值分块,查询时跳着查,到第k所在的那个块时再一个一个查,这样就可以O\left(\sqrt{n} \right )地进行查询了。

而第二个操作要求对这个进行可持久化。我们发现我们对刚做的一个链接操作进行撤回是十分容易的,于是我们可以把所有的操作建成一棵树,如果是返回操作就接在它返回的操作的点上,否则就接在上一个操作上,我们执行时只需要查询遍历这棵树就可以了。

然后我们便可以用O\left(nlog_{n}\sqrt{n} \right )的时间复杂度解决这道题了。虽说这时间复杂度很丑。

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<stack>
using namespace std;
typedef long long LL;
const int mo=1e9+7;
#define MAXN 100005
const int MAXM=1200;
typedef pair<int,int> pii;
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
int n,n1,n2,m,a[MAXN],id[MAXN],c[MAXN];
int fa[MAXN],dis[MAXN],sum[MAXN][MAXN/MAXM+5];
int opt[MAXN],X[MAXN],Y[MAXN],ans[MAXN];
vector<int> vec[MAXN];
bool cmp(int x,int y){return a[x]<a[y];}
void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i,dis[i]=1;}
int findSet(int x){if(fa[x]==x)return x;return findSet(fa[x]);}
int query(int x,int y){
	if(y>dis[x])return 0;
	int t=0;while(y>sum[x][t])y-=sum[x][t++];
	for(int i=0;i<MAXM;i++){
		if(findSet(id[t*MAXM+i])==x)y--;
		if(!y)return id[MAXM*t+i];
	}
}
void dfs(int x){
	//printf("%d:%d\n",x,opt[x]);
	if(opt[x]==1){
		int u=findSet(X[x]),v=findSet(Y[x]);
		if(dis[u]>dis[v])swap(u,v);//puts("a");
		if(u!=v){
			fa[u]=v;dis[v]+=dis[u];
			for(int i=0;i<=n2;i++)sum[v][i]+=sum[u][i];
		}
		for(int i=0;i<vec[x].size();i++)dfs(vec[x][i]);
		if(u!=v){
			fa[u]=u;dis[v]-=dis[u];
			for(int i=0;i<=n2;i++)sum[v][i]-=sum[u][i];
		}
	}
	else{
		if(opt[x]==3)ans[x]=query(findSet(X[x]),Y[x]);
		for(int i=0;i<vec[x].size();i++)dfs(vec[x][i]);
	}
}
signed main(){
	read(n);read(m);a[0]=-1;n2=n/MAXM+1;
	for(int i=1;i<=n;i++)read(a[i]),id[i]=i;
	sort(id+1,id+n+1,cmp);makeSet(n);
	for(int i=1;i<=n;i++)c[i]=a[id[i]],sum[id[i]][i/MAXM]=1;
	for(int i=1;i<=m;i++){
		read(opt[i]);read(X[i]);
		if(opt[i]==2)vec[X[i]].push_back(i);
		else read(Y[i]),vec[i-1].push_back(i);
	}
	dfs(0);for(int i=1;i<=m;i++)if(opt[i]==3)printf("%d\n",a[ans[i]]);
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值