CF757G Can Bash Save the Day?【边分树】

给定 \(n\) 个点的树(边带权)和长为 \(n\) 的排列 \(a\)\(q\) 次操作 \(\texttt{1 l r v}\) 询问 \(\sum_{i=l}^r\text{dis}(a_i,v)\)\(\texttt{2 x}\) 表示 swap(a[x],a[x+1])。强制在线。

\(n,q\leq 2\cdot 10^5\)


可持久化边分树。。。

行,叫我练 DS 我就写!

本质上是边分树合并,类似线段树合并,边分树的结构本身是确定的,维护的是每个节点的信息。

建立 \(n\) 棵边分树,第 \(i\) 棵表示 \(a_1,a_2,\cdots,a_i\) 的信息,查询就差分一下,拆成 \(\sum_{i=1}^r\text{dis}(a_i,v)\),枚举 lca 计算贡献,修改就重构第 \(x\) 棵边分树。

预处理的时候边分树不需完整建出来,只需对每个点处理出其在每一层是左儿子还是右儿子,以及到分治中心的距离。

时空复杂度 \(O((n+q)\log n)\)

#include<bits/stdc++.h>
#include<vector>
#define PB emplace_back
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 200003, M = N*70, B = (1<<30)-1;
template<typename T>
void rd(T &x){
	int ch = getchar(); x = 0;
	for(;ch < '0' || ch > '9';ch = getchar());
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
int n, q, nc, p[N], head[N<<1], to[N<<2], nxt[N<<2], we[N<<2];
vector<pii> G[N];
void add(int a, int b, int c){
	static int cnt = 1;
	to[++cnt] = b; nxt[cnt] = head[a]; head[a] = cnt; we[cnt] = c;
	to[++cnt] = a; nxt[cnt] = head[b]; head[b] = cnt; we[cnt] = c;
}
void reb(int x, int fa){
	for(auto it = G[x].begin();it != G[x].end();++ it)
		if(it->fi == fa){G[x].erase(it); break;}
	for(pii _ : G[x]) reb(_.fi, x);
	while(G[x].size() > 2){
		pii _ = G[x].back(); G[x].pop_back();
		add(++nc, _.fi, _.se);
		_ = G[x].back(); G[x].pop_back();
		add(nc, _.fi, _.se);
		G[x].PB(nc, 0);
	}
	for(pii _ : G[x]) add(x, _.fi, _.se);
}
int all, tmp, rte, siz[N<<1], dep[N];
bool vis[N<<2], opt[N][23];
LL dis0[N][23], dis1[N][23];
void fdrt(int x, int fa){
	siz[x] = 1;
	for(int i = head[x], v;i;i = nxt[i]){
		if(vis[i] || (v = to[i]) == fa) continue;
		fdrt(v, x); siz[x] += siz[v];
		if(chmax(tmp, min(siz[v], all-siz[v])))
			rte = i;
	}
}
void calc(int x, int fa, LL dis, int d, bool op){
	if(x <= n){
		opt[x][d] = op;
		dis0[x][d] = dis;
		dis1[x][d] = dis+we[rte];
	}
	for(int i = head[x];i;i = nxt[i])
		if(!vis[i] && to[i] != fa)
			calc(to[i], x, dis+we[i], d, op);
}
void dac(int x, int d, int S){
	if(S <= 1){
		if(x <= n) dep[x] = d; return;
	} all = S; tmp = 0; fdrt(x, 0);
	int u = to[rte], v = to[rte^1];
	vis[rte] = vis[rte^1] = true;
	calc(u, v, 0, d, 0);
	calc(v, u, 0, d, 1);
	dac(v, d+1, S-siz[u]);
	dac(u, d+1, siz[u]);
}
int tot, rt[N], ls[M], rs[M], ct[M][2];
LL sm[M][2], lans;
void merg(int &x, int y, int d, int u){
	if(d >= dep[u]) return;
	x = ++tot;
	ls[x] = ls[y]; rs[x] = rs[y];
	ct[x][0] = ct[y][0]; ct[x][1] = ct[y][1];
	sm[x][0] = sm[y][0]; sm[x][1] = sm[y][1];
	bool op = opt[u][d];
	sm[x][op] += dis0[u][d]; ++ct[x][op];
	if(op) merg(rs[x], rs[y], d+1, u);
	else merg(ls[x], ls[y], d+1, u);
}
void qry(int x, int y, int d, int u){
	if(d >= dep[u]) return;
	bool op = opt[u][d];
	lans += sm[y][!op] - sm[x][!op] + dis1[u][d]*(ct[y][!op]-ct[x][!op]);
	if(op) qry(rs[x], rs[y], d+1, u);
	else qry(ls[x], ls[y], d+1, u);
}
int main(){
	rd(n); rd(q); nc = n; 
	for(int i = 1;i <= n;++ i) rd(p[i]);
	for(int i = 1, u, v, w;i < n;++ i){
		rd(u); rd(v); rd(w);
		G[u].PB(v, w); G[v].PB(u, w);
	} reb(1, 0); dac(1, 0, nc);
	for(int i = 1;i <= n;++ i)
		merg(rt[i], rt[i-1], 0, p[i]);
	while(q --){
		int op, l, r, x; rd(op);
		if(op == 1){
			rd(l); rd(r); rd(x);
			l ^= lans; r ^= lans; x ^= lans;
			lans = 0; qry(rt[l-1], rt[r], 0, x);
			printf("%lld\n", lans); lans &= B;
		} else {
			rd(x); x ^= lans; swap(p[x], p[x+1]);
			merg(rt[x], rt[x-1], 0, p[x]);
		}
	}
}
posted @ 2021-06-21 11:44  mizu164  阅读(70)  评论(0编辑  收藏  举报