P4299 首都

P4299 首都

容易发现,这个难啊,要动态维护重心,有一个 \(\color {black} {\text {n}} \color {red} {\text{aive}}\) 的想法就是直接 \(O (n \log^2 n)\) 启发式合并,但是我们的 \(\color {black} {\text {Y}} \color {red} {\text {outh518}}\) 大佬直接利用LCT爆切。

可以发现的是,如果两个树合并,那么他们的重心必定在原来两个树重心的路径上。那么我们其实可以合并后split一下利用splay来跳,可以快速得到重心的位置,这个复杂度可以做到 \(O(n \log n)\)

/*
	Name:
	Author: Gensokyo_Alice
	Date:
	Description:
*/
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <map>
#include <set>

using namespace std;

typedef long long ll;
const ll MAXN = (1LL << 20) + 10, INF = 0x3f3f3f3f3f3f3f3f;

ll N, M, ans, r[MAXN], c[MAXN][2], fa[MAXN], st[MAXN], s[MAXN], si[MAXN], f[MAXN];

void link(ll, ll);
void access(ll);
void split(ll, ll);
void cut(ll, ll);
void makeroot(ll);
ll findroot(ll);
void splay(ll);
void rotate(ll);
ll find_(ll);
ll updata(ll);
void pushup(ll);
void pushr(ll);
void pushdown(ll);

int main() {
	scanf("%lld%lld", &N, &M);
	for (ll i = 1; i <= N; i++) s[i] = 1, f[i] = i, ans ^= i;
	for (ll i = 1, x, y; i <= M; i++) {
		char s[10]; 
		scanf("%s", s+1);
		if (s[1] == 'A') {
			scanf("%lld%lld", &x, &y);
			link(x, y);
			x = find_(x), y = find_(y);
			split(x, y);
			ll z = updata(y);
			ans = ans ^ x ^ y ^ z;
			f[x] = f[y] = f[z] = z;
		} else if (s[1] == 'Q') {
			scanf("%lld", &x);
			printf("%lld\n", find_(x));
		} else if (s[1] == 'X') printf("%lld\n", ans);
	}
    return 0;
}

void access(ll x) {for (ll y = 0; x; y = x, x = fa[x]) splay(x), si[x] += s[c[x][1]], si[x] -= s[c[x][1] = y], pushup(x);}
void makeroot(ll x) {access(x); splay(x); pushr(x);}
void link(ll x, ll y) {split(x, y); if (findroot(y) != x) si[fa[x] = y] += s[x];}
void split(ll x, ll y) {makeroot(x); access(y); splay(y);} 
void cut(ll x, ll y) {split(x, y); if (findroot(y) != x || fa[y] != x || c[y][0]) return; fa[y] = c[x][1] = 0;}
void pushr(ll x) {swap(c[x][0], c[x][1]); r[x] ^= 1;}
void pushup(ll x) {s[x] = s[c[x][0]] + s[c[x][1]] + si[x] + 1;}
void pushdown(ll x) {if (r[x]) {if (c[x][0]) pushr(c[x][0]); if (c[x][1]) pushr(c[x][1]);} r[x] = 0;}
bool nroot(ll x) {return c[fa[x]][0] == x || c[fa[x]][1] == x;}
ll findroot(ll x) {access(x); splay(x); while (c[x][0]) x = c[x][0]; return x;}
void splay(ll x) {
	ll y = x, z = 0; st[++z] = y;
	while (nroot(y)) st[++z] = (y = fa[y]); while (z) pushdown(st[z--]);
	while (nroot(x)) {
		y = fa[x], z = fa[y];
		if (nroot(y)) rotate(((c[y][0] == x) ^ (c[z][0] == y)) ? x : y);
		rotate(x);
	}
	pushup(x);
}
void rotate(ll x) {
	ll y = fa[x], z = fa[y], k = c[y][1] == x, w = c[x][!k];
	if (nroot(y)) c[z][c[z][1] == y] = x; c[x][!k] = y; c[y][k] = w;
	if (w) fa[w] = y; fa[y] = x; fa[x] = z;
	pushup(y);	
}

ll find_(ll x) {return f[x] == x ? x : f[x] = find_(f[x]);}
ll updata(ll node) {
	ll l, r, ji = s[node] & 1, sum = s[node] >> 1, lsum = 0, rsum = 0, nowl, nowr, tar = INF;
	while (node) {
		pushdown(node);
		nowl = s[l = c[node][0]] + lsum, nowr = s[r = c[node][1]] + rsum;
		if (nowl <= sum && nowr <= sum) {
			if (ji & 1) {tar = node; break;}
			else if (tar > node) tar = node;
		}
		if (nowl < nowr) lsum += s[l] + si[node] + 1, node = r;
		else rsum += s[r] + si[node] + 1, node = l;
	}
	splay(tar);
	return tar;
}

posted @ 2021-01-05 15:53  Gensokyo_Alice  阅读(145)  评论(0编辑  收藏  举报