zoukankan      html  css  js  c++  java
  • CF757G Can Bash Save the Day?(可持久化边分治)

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

    题目大意

    一棵 (n) 个点的树和一个排列 (p_i) ,边有边权,支持两种操作:

    • (l r x),询问 (sumlimits _{i=l}^{r} dis(p_i,x))
    • (x),交换 (p_x,p_{x+1})

    数据范围

    (n,qleq 2 imes 10^5),强制在线

    解题思路

    牛逼题

    显然这是一道老经典题了,考虑开店那题,直接动态点分治上整个前缀和,二分查找即可

    考虑有修改,每个点整个平衡树或者动态开点线段树即可,都是 (Theta(nlog^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;
    }
    
    
  • 相关阅读:
    CSP2019-S2总结
    #期望dp#洛谷 6835 [Cnoi2020]线形生物
    #树状数组,概率,离散,双指针#洛谷 6834 [Cnoi2020]梦原
    #容斥,倍数,排列组合#洛谷 2714 四元组统计
    #莫队,bitset#洛谷 3674 小清新人渣的本愿
    #贪心#洛谷 3173 [HAOI2009]巧克力
    #树链剖分,LCA#洛谷 3398 仓鼠找sugar
    #树状数组,哈希#洛谷 6687 论如何玩转 Excel 表格
    小甲鱼Python第八讲课后习题
    小甲鱼Python第七讲课后习题
  • 原文地址:https://www.cnblogs.com/Hs-black/p/13366263.html
Copyright © 2011-2022 走看看