luogu P5064 [Ynoi2014] 等这场战争结束之后
https://www.luogu.com.cn/problem/P5064
挺有启发性的一道题,可惜我不太会树分块
显然先离线下来
因为有撤回操作,换个思路,可以对询问建树
1,3操作就是
(
i
−
1
)
−
>
(
i
)
(i-1)->(i)
(i−1)−>(i)
2操作是
x
−
>
i
x->i
x−>i
于是我门需要一个支持撤回的数据结构来维护连通性和其他信息
连通性显然可以搞一个启发式合并并查集来做
但是联通块第
y
y
y小并没有什么比较好的数据结构可以polylog维护
所以考虑离散化后值域分块
每一块记录每个位置当前的联通块 中在这个权值区间的数的个数
连边就把
x
,
y
x,y
x,y的每一块加过去
撤回就减回去,很简单
询问就按照一般的做法做,注意相同权值的数的处理
这样的空间复杂度是
O
(
n
n
)
O(n\sqrt{n})
O(nn)的
时间复杂度貌似要多个log
然而空间会被卡,时间也有点慢
所以可以把块数调小一些,块长调大一些,这样就可以把log再均摊到根号里面,空间也可以减少
最后时间复杂度应该是
O
(
n
n
l
o
g
n
O(n\sqrt{nlogn}
O(nnlogn的
块数我调的是35,最慢300+ms
玄学
code:
#include<bits/stdc++.h>
#define N 100005
using namespace std;
struct edge {
int v, nxt;
} e[N];
int p[N], eid;
void init() {
memset(p, -1, sizeof p);
eid = 0;
}
void insert(int u, int v) {
e[eid].v = v;
e[eid].nxt = p[u];
p[u] = eid ++;
}
struct A {
int x, y;
} a[N];
int cmp(A x, A y) {
return x.x < y.x;
}
short cnt[N][36];
int n, m, X[N], Y[N], O[N], size[N], fa[N], K, blo, ans[N];
int get(int x) { return x == fa[x]? x : get(fa[x]); }
void dfs(int u) {
int f = 0;
int o = O[u], &x = X[u], &y = Y[u];
if(o == 1) {
x = get(x), y = get(y);
if(x != y) {
f = 1;
if(size[x] > size[y]) swap(x, y);
fa[x] = y; size[y] += size[x];
for(int i = 1; i <= K; i ++) cnt[y][i] += cnt[x][i];
}
} else if(o == 3) {
x = get(x);
if(y > size[x]) ans[u] = -1;
else {
int id = 0;
for(int i = 1; i <= K; i ++) {
if(y > cnt[x][i]) y -= cnt[x][i];
else {
id = i;
break;
}
}
for(int i = (id - 1) * blo + 1; i <= min(id * blo, n); i ++) {
if(get(a[i].y) == x) y --, ans[u] = a[i].x;
if(!y) break;
}
}
}
for(int i = p[u]; i + 1; i = e[i].nxt) dfs(e[i].v);
if(f) {
for(int i = 1; i <= K; i ++) cnt[y][i] -= cnt[x][i];
fa[x] = x, size[y] -= size[x];
}
}
int main() {
init();
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i].x), a[i].y = i;
sort(a + 1, a + 1 + n, cmp);
K = 35; blo = n / K + 1; K = (n - 1) / blo + 1;
for(int i = 1; i <= n; i ++) {
int bel = (i - 1) / blo + 1;
cnt[a[i].y][bel] ++;
size[i] = 1; fa[i] = i;
}
for(int i = 1; i <= m; i ++) {
scanf("%d", &O[i]);
if(O[i] == 1) {
scanf("%d%d", &X[i], &Y[i]);
insert(i - 1, i);
}
else if(O[i] == 2) {
scanf("%d", &X[i]);
insert(X[i], i);
} else {
scanf("%d%d", &X[i], &Y[i]);
insert(i - 1, i);
}
}
dfs(0);
for(int i = 1; i <= m; i ++) if(O[i] == 3) printf("%d\n", ans[i]);
return 0;
}