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]);
}
}
}