【生物】分類(洛谷跨年夜場E題)(樹換根+樹剖)

【生物】分類

這場比賽拿了個B題一血,舒服!
在這裏插入圖片描述

題意:模板題

給定一張連通圖,求出以1爲根的最小生成樹(然後就跟圖沒啥關係了)。
對於這棵生成樹,有3種操作+3種詢問:

  1. 更換根節點
  2. 樹上xxyy的最短路徑上的點權加dd
  3. 樹上xx所在子樹所有節點點權加dd
  4. xxyyLCALCA
  5. xxyy的最短路徑上的點權之和
  6. xx所在子樹所有節點點權之和

思路:關鍵在於換根以及求lcalca

這個換根想了挺久都沒有想法,實在沒有正確複雜度的操作,真是太菜!
於是百度了一下,發現換根是種傳統操作(板子),實際處理方法就是換根但不換樹剖的信息!
(前置知識:樹剖,並且樹剖的同時記錄dfsdfs序的idleftid_{left}idrightid_{right})(以下所有“子樹”是指在以1爲根的樹中)

  1. 換根
    直接用一個rootroot記錄當前根節點即可,就這麼簡單!當然這裏方便了後面就會麻煩一些。
  2. 樹上xxyy的最短路徑上的點權加dd
    由於樹上最短路徑與根是哪個點無關,因此正常的做就好了。
  3. 樹上xx所在子樹所有節點點權加dd
    考慮兩種情況+一種特判:
    • 特判:若x=rootx=root,那直接整棵樹加dd
    • rootroot不在xx子樹中,則顯然根具體是哪個點不會影響這棵子樹
    • rootrootxx子樹中,此時這棵子樹會發生變化,真正的子樹變成了屬於以當前點向“上”的樹,具體而言就是整棵樹去掉原子樹中rootroot所在的“樹枝”後都是“xx所在的子樹”。
      然後問題就變成了如何找到這個“樹枝”,我這裏採用的倍增法,從rootroot倍增跳fatherfather,跳到剛好是xx的直接兒子即可,複雜度O(log2n)O(log_{2}n)
  4. xxyyLCALCA
    這是本題難點,有不止一種方法,自認爲我這方法挺不錯(題解方法分類太多了),嘿嘿
    • xxyy都屬於原rootroot子樹,則返回原lcalca即可(很顯然啦)
    • 若一個屬於,一個不屬於,則lcalca就是rootroot,因爲它夾在中間了
    • 若兩個都不屬於則分兩種情況:
      • 先考慮以二者的lcalca爲根的原子樹是否包含rootroot,若不包含,則顯然返回lcalca即可
      • 剩下的就是不包含的情況,我們將xxyy分別用倍增跳fatherfather,都跳到剛好使rootroot在當前原子樹中即可,然後將最後的xxyy取深度較大的(爲了更接近rootroot
  5. xxyy的最短路徑上的點權之和
    同2,樹上最短路徑與根是哪個點無關
  6. xx所在子樹所有節點點權之和
    同3,考慮兩種情況+一種特判即可。

Ok, all right! 此題不失爲一道換根+樹剖板子好題!複雜度目測爲O(nlog22n)\displaystyle O(nlog_2^2{n}),集中在樹剖+線段樹區間操作處,已忽略一開始的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()));
    }
}

出題人題解鏈接

他的lcalca分類確實比我麻煩些。

發佈了124 篇原創文章 · 獲贊 21 · 訪問量 8184
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章