1. 程式人生 > 實用技巧 >CF757G Can Bash Save the Day?(可持久化邊分治)

CF757G Can Bash Save the Day?(可持久化邊分治)

CF757G Can Bash Save the Day?(可持久化邊分治)

題目大意

一棵 \(n\) 個點的樹和一個排列 \(p_i\) ,邊有邊權,支援兩種操作:

  • \(l\ r\ x\),詢問 \(\sum\limits _{i=l}^{r} dis(p_i,x)\)
  • \(x\),交換 \(p_x,p_{x+1}\)

資料範圍

\(n,q\leq 2\times 10^5\),強制線上

解題思路

牛逼題

顯然這是一道老經典題了,考慮開店那題,直接動態點分治上整個字首和,二分查詢即可

考慮有修改,每個點整個平衡樹或者動態開點線段樹即可,都是 \(\Theta(n\log^2n)\) 的,其中第一個做法常數大,第二個做法空間大,都過不去

還有另外一種思路,考慮所有距離和等於 \(\sum (dep_x+dep_y)-2*\sum dep_{lca(x,y)}\)

前面那個很好求,後面那個考慮樹鏈剖分,可以想象為先把 y 到根的鏈打上標記,然後從 x 跳到根路徑上的標記長度就是 \(dep_{lca(x,y)}\),具體來說打標記的方式可以維護每一條邊被覆蓋的次數,然後乘上整體的區間長度即可,把線段樹換成主席樹標記永久化即可實現靜態,考慮如何交換相鄰兩項,由於是和的形式,容易發現後面一項一直到最後一項的主席樹沒有變化,只需要把第 x 個主席樹重構一下即可,但顯然這個做法雖然常數小了一點,但時間複雜度和空間複雜度還是爆炸的

考慮邊分治這種神仙做法,邊分治可以可持久化!當然點分治也可以但是比邊分治要麻煩一些吧

可以看看暴力寫掛那題,邊分樹是可以合併的,那麼也意味著它可以支援可持久化,就像線段樹一樣

每個點當葉子都可以取出一條從根(邊分治的根)到它的鏈,我們取出待用,取原題排列一個,按順序將所有鏈輕輕合併,查詢時用第 r 棵邊分樹減去第 l - 1 邊分樹的答案即可,交換相鄰兩項時直接重構前一棵主席邊分樹即可

我們邊分樹真的是太厲害了

經檢驗,程式碼可讀

const int N = 500500;
struct Tree {
	int ls, rs;
	ll vl, vr, sl, sr;
	#define sl(p) tree[p].sl
	#define sr(p) tree[p].sr
	#define ls(p) tree[p].ls
	#define rs(p) tree[p].rs
	#define vl(p) tree[p].vl
	#define vr(p) tree[p].vr
}tree[N * 32];

int rt[N], nodecnt, cnt, n, q;
namespace Conquer {
	const int N = 1500050;
	int h[N], ne[N<<1], to[N<<1], w[N<<1], tot = 1;
	inline void add(int x, int y, int z) {
		ne[++tot] = h[x], to[h[x] = tot] = y, w[tot] = z;
	}
	int Siz, siz[N], vis[N], las[N], lim, ed;
	inline void adde(int x, int y, int z) { 
		add(x, y, z), add(y, x, z); 
//		write(x, ' '), write(y, ' '), write(z); 
	}
	
	void get(int x, int fa) {
		siz[x] = 1;
		for (int i = h[x], y; i; i = ne[i]) {
			if ((y = to[i]) == fa || vis[i]) continue;
			get(y, x), siz[x] += siz[y];
			int tp = max(siz[y], Siz - siz[y]);
			if (tp < lim) lim = tp, ed = i;
		}
	}
	
	void dfs(int x, int fa, ll Dis, int ty) {
		if (x <= n) {
			++nodecnt, !rt[x] && (rt[x] = nodecnt);
			if (ls(las[x]) == -1) ls(las[x]) = nodecnt;
			else rs(las[x]) = nodecnt;
			if (!ty) ls(nodecnt) = -1, vl(nodecnt) = Dis, sl(nodecnt)++;
			else rs(nodecnt) = -1, vr(nodecnt) = Dis, sr(nodecnt)++;
			las[x] = nodecnt;
		}
		for (int i = h[x], y; i; i = ne[i])
			if ((y = to[i]) != fa && !vis[i])
				dfs(y, x, Dis + w[i], ty);
	}
	
	void conquer(int x, int S) {
		if (S <= 1) return;
		Siz = lim = S, get(x, 0), vis[ed] = vis[ed ^ 1] = 1;
		int tx = to[ed], ty = to[ed ^ 1];
//		write(tx, ' '), write(ty);
		dfs(tx, 0, 0, 0), dfs(ty, 0, w[ed], 1);
		conquer(ty, S - siz[tx]), conquer(tx, siz[tx]);
	}
}

int h[N], ne[N<<1], to[N<<1], w[N<<1], las[N], tot;
inline void add(int x, int y, int z) {
	ne[++tot] = h[x], to[h[x] = tot] = y, w[tot] = z;
}

void dfs(int x, int fa) {
	for (int i = h[x], y; i; i = ne[i]) {
		if ((y = to[i]) == fa) continue; dfs(y, x);
		if (!las[x]) Conquer::adde(las[x] = x, y, w[i]);
		else {
			Conquer::adde(las[x], ++cnt, 0);
			Conquer::adde(las[x] = cnt, y, w[i]);
		}
	}
}

int update(int pre, int nw) {
	int rt = ++nodecnt; tree[rt] = tree[pre];
	vl(rt) += vl(nw), vr(rt) += vr(nw), sl(rt) += sl(nw), sr(rt) += sr(nw);
	if (sl(nw) > 0) ls(rt) = update(ls(rt), ls(nw));
	if (sr(nw) > 0) rs(rt) = update(rs(rt), rs(nw));
	return rt;
}

ll query(int nw, int p1, int p2) {
	if (sl(nw) > 0) 
		return query(ls(nw), ls(p1), ls(p2)) + vr(p2) - vr(p1) + (sr(p2) - sr(p1)) * vl(nw);
	if (sr(nw) > 0) 
		return query(rs(nw), rs(p1), rs(p2)) + vl(p2) - vl(p1) + (sl(p2) - sl(p1)) * vr(nw);
	return 0;
}

const int B = (1 << 30) - 1;
ll ans, T[N], p[N];
int main() {
	read(n), read(q), cnt = n;
	for (int i = 1;i <= n; i++) read(p[i]);
	for (int i = 1, x, y, z;i < n; i++)
		read(x), read(y), read(z), add(x, y, z), add(y, x, z);
	dfs(1, 0), Conquer::conquer(1, cnt), tree[0] = tree[nodecnt+10];
	for (int i = 1;i <= nodecnt; i++) Mx(ls(i), 0), Mx(rs(i), 0);
	for (int i = 1;i <= n; i++) T[i] = update(T[i-1], rt[p[i]]);
	while (q--) {
		ll op, x, y, z; read(op);
		if (op == 1) {
			read(x), read(y), read(z);
			x = ans ^ x, y ^= ans, z ^= ans;
			write(ans = query(rt[z], T[x - 1], T[y]));
			ans &= B;
		}
		else {
			read(x), x ^= ans, swap(p[x], p[x+1]);
			T[x] = update(T[x-1], rt[p[x]]);
		}
	}
	return 0;
}