题意:模板题
给定一张连通图,求出以1为根的最小生成树(然后就跟图没啥关系了)。
对于这棵生成树,有3种操作+3种询问:
思路:关键在于换根以及求 l c a lca lca
这个换根想了挺久都没有想法,实在没有正确复杂度的操作,真是太菜!
于是百度了一下,发现换根是种传统操作(板子),实际处理方法就是换根但不换树剖的信息!
(前置知识:树剖,并且树剖的同时记录 d f s dfs dfs序的 i d l e f t id_{left} idleft和 i d r i g h t id_{right} idright)(以下所有“子树”是指在以1为根的树中)
Ok, all right! 此题不失为一道换根+树剖板子好题!复杂度目测为 O ( n l o g 2 2 n ) \displaystyle O(nlog_2^2{n}) O(nlog22n),集中在树剖+线段树区间操作处,已忽略一开始的MST复杂度。
#include "bits/stdc++.h"
#define hhh printf("hhh\n")
#define see(x) (cerr<<(#x)<<'='<<(x)<
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
inline int read() {
int x=0,f=1;char c=getchar();while(c!='-'&&(c<'0'||c>'9'))c=getchar();if(c=='-')f=-1,c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return f*x;}
const int maxn = 3e5+7;
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
struct Edge{
int u, v, w, id;
bool operator < (const Edge &rhs) const {
if(w==rhs.w) return id<rhs.id;
return w<rhs.w;
}
}e[maxn*2];
int n, m, r, root=1;
int a[maxn], belong[maxn];
int head[maxn], to[maxn*2], nxt[maxn*2], tot;
int lid[maxn], rid[maxn], rk[maxn], ID;
int son[maxn], top[maxn], sz[maxn], deep[maxn];
int fa[maxn], st[maxn][20];
int find(int a) {
return a==belong[a]?a:belong[a]=find(belong[a]); }
inline void add_edge(int u, int v) {
++tot; to[tot]=v; nxt[tot]=head[u]; head[u]=tot;
++tot; to[tot]=u; nxt[tot]=head[v]; head[v]=tot;
}
void dfs0(int u, int f, int d) {
fa[u]=f; sz[u]=1; deep[u]=d;
for(int i=head[u]; i; i=nxt[i]) {
int v=to[i]; if(v==f) continue;
dfs0(v,u,d+1); sz[u]+=sz[v];
if(sz[v]>sz[son[u]]) son[u]=v;
}
}
void dfs(int u, int f, int t) {
rk[lid[u]=++ID]=u; top[u]=t;
if(son[u]) dfs(son[u],u,t);
for(int i=head[u]; i; i=nxt[i]) {
int v=to[i]; if(v==f||v==son[u]) continue;
dfs(v,u,v);
}
rid[u]=ID;
}
ll node[maxn<<2], lazy[maxn<<2];
void build(int l, int r, int now) {
if(l==r) {
node[now]=a[rk[l]];
return;
}
int m=(l+r)/2;
build(l,m,now<<1); build(m+1,r,now<<1|1);
node[now]=node[now<<1]+node[now<<1|1];
}
void push_down(int l, int r, int now) {
ll &d=lazy[now], m=(l+r)/2;
node[now<<1]+=ll(m-l+1)*d; lazy[now<<1]+=d;
node[now<<1|1]+=ll(r-m)*d; lazy[now<<1|1]+=d;
d=0;
}
void update(int x, int y, int d, int l, int r, int now) {
if(x>y) return;
if(x<=l&&r<=y) {
node[now]+=ll(r-l+1)*d;
lazy[now]+=d;
return;
}
if(lazy[now]) push_down(l,r,now);
int m=(l+r)/2;
if(x<=m) update(x,y,d,l,m,now<<1);
if(y>m) update(x,y,d,m+1,r,now<<1|1);
node[now]=node[now<<1]+node[now<<1|1];
}
ll query(int x, int y, int l, int r, int now) {
if(x>y) return 0;
if(x<=l&&r<=y) return node[now];
if(lazy[now]) push_down(l,r,now);
int m=(l+r)/2;
ll ans=0;
if(x<=m) ans+=query(x,y,l,m,now<<1);
if(y>m) ans+=query(x,y,m+1,r,now<<1|1);
return ans;
}
int lca(int x, int y) {
if(deep[x]<deep[y]) swap(x,y);
for(int i=19; i>=0; --i) if(deep[st[x][i]]>=deep[y]) x=st[x][i];
if(x==y) return x;
for(int i=19; i>=0; --i) if(st[x][i]!=st[y][i]) x=st[x][i], y=st[y][i];
return fa[x];
}
bool contain(int x, int r) {
return lid[r]<=lid[x]&&lid[x]<=rid[r];
}
void add_xyd(int x, int y, int d) {
while(top[x]!=top[y]) {
if(deep[top[x]]<deep[top[y]]) swap(x,y);
update(lid[top[x]],lid[x],d,1,n,1); x=fa[top[x]];
}
if(deep[x]<deep[y]) swap(x,y);
update(lid[y],lid[x],d,1,n,1);
}
void add_xd(int x, int d) {
if(root==x) update(1,n,d,1,n,1);
else if(!contain(root,x)) update(lid[x],rid[x],d,1,n,1);
else {
int r=root;
for(int i=19; i>=0; --i) if(deep[st[r][i]]>deep[x]) r=st[r][i];
update(1,lid[r]-1,d,1,n,1);
update(rid[r]+1,n,d,1,n,1);
}
}
int get_lca(int x, int y) {
int cas=contain(x,root)+contain(y,root);
if(cas==2) return lca(x,y);
if(cas==1) return root;
int LCA=lca(x,y);
if(!contain(root,LCA)) return LCA;
for(int i=19; i>=0; --i) {
if(st[x][i]&&!contain(root,st[x][i])) x=st[x][i];
if(st[y][i]&&!contain(root,st[y][i])) y=st[y][i];
}
if(!contain(root,x)) x=fa[x];
if(!contain(root,y)) y=fa[y];
return deep[x]>=deep[y]?x:y;
}
ll sum_xy(int x, int y) {
ll ans=0;
while(top[x]!=top[y]) {
if(deep[top[x]]<deep[top[y]]) swap(x,y);
ans+=query(lid[top[x]],lid[x],1,n,1); x=fa[top[x]];
}
if(deep[x]<deep[y]) swap(x,y);
ans+=query(lid[y],lid[x],1,n,1);
return ans;
}
ll sum_x(int x) {
if(root==x) return node[1];
if(!contain(root,x)) return query(lid[x],rid[x],1,n,1);
int r=root;
for(int i=19; i>=0; --i) if(deep[st[r][i]]>deep[x]) r=st[r][i];
return query(1,lid[r]-1,1,n,1)+query(rid[r]+1,n,1,n,1);
}
int main() {
//freopen("testin", "r", stdin);
//freopen("testout", "w", stdout);
n=read(), m=read(), r=read();
for(int i=1; i<=n; ++i) a[i]=read(), belong[i]=i;
for(int i=1; i<=r; ++i) {
int u=read(), v=read(), w=read();
e[i]=(Edge){
u,v,w,i};
}
sort(e+1,e+1+r);
for(int i=1; i<=r; ++i) {
int x=find(e[i].u);
int y=find(e[i].v);
if(x!=y) belong[x]=y, add_edge(e[i].u,e[i].v);
}
dfs0(1,0,1);
dfs(1,0,1);
for(int j=0; j<=19; ++j) {
for(int i=1; i<=n; ++i)
if(!j) st[i][j]=fa[i];
else st[i][j]=st[st[i][j-1]][j-1];
}
build(1,n,1);
while(m--) {
int op=read();
if(op==1) root=read();
else if(op==2) {
int x=read(), y=read(), d=read();
add_xyd(x,y,d);
}
else if(op==3) {
int x=read(), d=read();
add_xd(x,d);
}
else if(op==4) {
int LCA=get_lca(read(),read());
printf("%lld\n", query(lid[LCA],lid[LCA],1,n,1));
}
else if(op==5) printf("%lld\n", sum_xy(read(),read()));
else if(op==6) printf("%lld\n", sum_x(read()));
}
}
他的 l c a lca lca分类确实比我麻烦些。