【生物】分類
這場比賽拿了個B題一血,舒服!
題意:模板題
給定一張連通圖,求出以1爲根的最小生成樹(然後就跟圖沒啥關係了)。
對於這棵生成樹,有3種操作+3種詢問:
- 更換根節點
- 樹上到的最短路徑上的點權加
- 樹上所在子樹所有節點點權加
- 求和的
- 求到的最短路徑上的點權之和
- 求所在子樹所有節點點權之和
思路:關鍵在於換根以及求
這個換根想了挺久都沒有想法,實在沒有正確複雜度的操作,真是太菜!
於是百度了一下,發現換根是種傳統操作(板子),實際處理方法就是換根但不換樹剖的信息!
(前置知識:樹剖,並且樹剖的同時記錄序的和)(以下所有“子樹”是指在以1爲根的樹中)
- 換根
直接用一個記錄當前根節點即可,就這麼簡單!當然這裏方便了後面就會麻煩一些。 - 樹上到的最短路徑上的點權加
由於樹上最短路徑與根是哪個點無關,因此正常的做就好了。 - 樹上所在子樹所有節點點權加
考慮兩種情況+一種特判:- 特判:若,那直接整棵樹加
- 不在子樹中,則顯然根具體是哪個點不會影響這棵子樹
- 在子樹中,此時這棵子樹會發生變化,真正的子樹變成了屬於以當前點向“上”的樹,具體而言就是整棵樹去掉原子樹中所在的“樹枝”後都是“所在的子樹”。
然後問題就變成了如何找到這個“樹枝”,我這裏採用的倍增法,從倍增跳,跳到剛好是的直接兒子即可,複雜度
- 求和的
這是本題難點,有不止一種方法,自認爲我這方法挺不錯(題解方法分類太多了),嘿嘿- 若和都屬於原子樹,則返回原即可(很顯然啦)
- 若一個屬於,一個不屬於,則就是,因爲它夾在中間了
- 若兩個都不屬於則分兩種情況:
- 先考慮以二者的爲根的原子樹是否包含,若不包含,則顯然返回即可
- 剩下的就是不包含的情況,我們將和分別用倍增跳,都跳到剛好使在當前原子樹中即可,然後將最後的和取深度較大的(爲了更接近)
- 求到的最短路徑上的點權之和
同2,樹上最短路徑與根是哪個點無關 - 求所在子樹所有節點點權之和
同3,考慮兩種情況+一種特判即可。
Ok, all right! 此題不失爲一道換根+樹剖板子好題!複雜度目測爲,集中在樹剖+線段樹區間操作處,已忽略一開始的MST複雜度。
代碼
#include "bits/stdc++.h"
#define hhh printf("hhh\n")
#define see(x) (cerr<<(#x)<<'='<<(x)<<endl)
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()));
}
}
出題人題解鏈接
他的分類確實比我麻煩些。