W
e
l
c
o
m
e
: )

[Ynoi2014] 等这场战争结束之后

题传

非常暴力的做法:每个点维护一颗平衡树,然后启发式合并。

但是这样的暴力做法会被回溯操作卡飞天。

先建出一棵操作树,将回溯操作简化为回退一次操作。

思考平衡树的劣势在哪里,合并和询问的复杂度极其不平衡。

想到平衡复杂度,那就只好分块了。

每个点维护一个值域分块,记录 \(cnt_{i, j}\)\(i\) 点为根的子树中第 \(j\) 个值域块有多少个数。

连边和退边操作直接用可撤销并查集维护,合并/分裂时将两个根之间的贡献相加/减去。

询问时直接用根的 \(cnt\) 找到在哪个值域块,然后暴力遍历在找到值域块的所有数(包括不在当前树内的),注意离散化的时候不能去重,否则复杂度不正确。

那么你大概得到了一个时间 \(O(nB\log n)\),空间 \(O(\frac{n^2}{B})\) 的东西。

发现这题卡空间,把 \(B\) 调大一点即可。还是过不去的话,注意 \(\frac{n}{B}\) 不是很大,把 \(cnt\) 开 short 立省一半空间。

Code:

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <cctype>
#include <vector>
#include <queue>
#include <bitset>
#define vi vector<int>
#define pb push_back
#define mp make_pair
#define st first
#define nd second
using namespace std;
typedef long long ll;
typedef pair <int, int> Pii;
const int INF=0x3f3f3f3f;
const int cp=998244353;
inline int mod(int x){if(x>=cp) x-=cp;if(x<0) x+=cp;return x;}
inline void plust(int &x, int y){x=mod(x+y);return ;}
inline void minut(int &x, int y){x=mod(x-y);return ;}
inline int read(){
	char ch=getchar();int x=0, f=1;
	while(!isdigit(ch)){if(ch=='-') f=-1; ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x){
    if(x<0) putchar('-'), x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
inline int ksm(int a, int b=cp-2){
	int ret=1;
	for(; b; b>>=1, a=1ll*a*a%cp)
		if(b&1) ret=1ll*ret*a%cp;
	return ret;
}
const int N=1e5+5;
const int M=4000;
int n, m, X[N], Y[N], bid[N], L[25], R[25], a[N], id[N], ans[N];
int siz[N], f[N], p[N], num[N], vis[N];
short cnt[N][25];
int find(int x){return (x^f[x])?find(f[x]):f[x];}
vi G[N], rub;
void calc(int x, int y, int op){for(int i=bid[1]; i<=bid[n]; ++i) cnt[x][i]+=op*cnt[y][i];}
bool merge(int x, int y){
	int fx=find(x), fy=find(y);if(fx==fy) return 0;
	if(siz[fx]<siz[fy]) swap(fx, fy);
	rub.pb(fy);f[fy]=fx, siz[fx]+=siz[fy];
	calc(fx, fy, 1);
	return 1;
}
void reback(){
	int fy=rub.back();rub.pop_back();
	calc(f[fy], fy, -1), siz[f[fy]]-=siz[fy], f[fy]=fy;
}
int query(int x, int y){
	x=find(x);
	int i=0, s=0;for(; i<=bid[n]&&s+cnt[x][i]<y; ++i) s+=cnt[x][i];
	if(i>bid[n]) return -1;
	for(int j=L[i]; j<=R[i]; ++j) if(find(num[j])==x&&(++s)==y) return p[j]; 
	return 0;
}
void dfs(int x){
	bool flg=0;
	if(Y[x]>0) flg=merge(X[x], Y[x]);
	else if(Y[x]<0) ans[x]=query(X[x], -Y[x]); 
	for(auto v:G[x]) dfs(v);
	if(flg) reback();
}
signed main(){
	n=read(), m=read();
	for(int i=1; i<=n; ++i) 
		a[i]=p[i]=read(), siz[f[i]=i]=1;
	sort(p+1, p+n+1);
	for(int i=1, pos; i<=n; ++i) 
		pos=lower_bound(p+1, p+n+1, a[i])-p, 
		a[i]=pos+(vis[pos]++), num[a[i]]=i;//, printf("%d ", a[i]);puts("");
	for(int i=1, c=0; i<=n; i+=M, ++c)
		for(int j=0; j<M&&i+j<=n; ++j) 
			L[c]=i, R[c]=i+j, bid[i+j]=c;
	for(int i=1; i<=n; ++i) cnt[i][bid[a[i]]]=1;//printf("%d\n", bid[n]);
	for(int i=1, pos=0; i<=m; ++i){
		int op=read();
		if(op==1) X[i]=read(), Y[i]=read(), G[pos].pb(i), pos=id[i]=i;
		if(op==2) pos=id[i]=id[read()];
		if(op==3) X[i]=read(), Y[i]=-read(), G[pos].pb(i), pos=id[i]=i;
	}
	dfs(0);
	for(int i=1; i<=m; ++i) if(Y[i]<0) printf("%d\n", ans[i]);
	return 0;
}
posted @ 2023-02-10 19:38  127_127_127  阅读(23)  评论(0编辑  收藏  举报