支配树题集

$$支配树题集$$

巨佬证明
搭配着看

1.DAG上的支配树

①洛谷P2597
传送门
答案就是支配树上的节点子树大小-1

//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 1e5+7;
int n,degree[MAXN],depth[MAXN],par[MAXN][20],ans[MAXN];
vector<int> G[MAXN],node,from[MAXN],GG[MAXN];
void toposort(){
    queue<int> que;
    for(int i = 1; i <= n; i++) if(degree[i]==0){
        G[0].emplace_back(i);
        from[i].emplace_back(0);
        degree[i]++;
    }
    que.push(0);
    while(!que.empty()){
        int u = que.front();
        que.pop();
        for(int v : G[u]){
            degree[v]--;
            if(degree[v]==0){
                node.emplace_back(v);
                que.push(v);
            }
        }
    }
}
int LCA(int u, int v){
    if(depth[u]<depth[v]) swap(u,v);
    for(int i = 0; i < 20; i++) if((depth[u]-depth[v])&(1<<i)) u = par[u][i];
    if(u==v) return u;
    for(int i = 19; i >= 0; i--) if(par[u][i]!=par[v][i]){
        u = par[u][i];
        v = par[v][i];
    }
    return par[u][0];
}
void build(){
    for(int u : node){
        par[u][0] = from[u][0];
        for(int v : from[u]) par[u][0] = LCA(par[u][0],v);
        depth[u] = depth[par[u][0]]+1;
        for(int i = 1; par[u][i-1]; i++) par[u][i] = par[par[u][i-1]][i-1];
        GG[par[u][0]].emplace_back(u);
    }
}
void dfs(int u, int f){
    ans[u] = 1;
    for(int v : GG[u]){
        if(v==f) continue;
        dfs(v,u);
        ans[u] += ans[v];
    }
}
int main(){
    scanf("%d",&n);
    for(int i = 1; i <= n; i++){
        int x;
        while(scanf("%d",&x)&&x!=0){
            G[x].emplace_back(i);
            from[i].emplace_back(x);
            degree[i]++;
        }
    }
    toposort();
    build();
    dfs(0,0);
    for(int i = 1; i <= n; i++) printf("%d
",ans[i]-1);
    return 0;
}

②codeforces 757F
传送门
先求最短路,然后把所有处在最短路上的边保留,以s为根节点建支配树,答案就是根节点外子树大小的最大值

//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
typedef int_fast64_t LL;
const int MAXN = 2e5+7;
int n,m,s,degree[MAXN],depth[MAXN],par[MAXN][20],sz[MAXN],ans;
LL dist[MAXN];
vector<pair<int,int>> G[MAXN];
vector<int> reG[MAXN],node,from[MAXN],GG[MAXN];
void Dijkstra(){
    memset(dist,0x3f,sizeof(dist));
    dist[s] = 0;
    priority_queue<pair<LL,int>,vector<pair<LL,int>>,greater<pair<LL,int>>> que;
    que.push(make_pair(dist[s],s));
    while(!que.empty()){
        auto tp = que.top();
        que.pop();
        int u = tp.second;
        LL d = tp.first;
        if(dist[u]!=d) continue;
        for(auto e : G[u]){
            if(dist[u]+e.second<dist[e.first]){
                dist[e.first] = dist[u] + e.second;
                que.push(make_pair(dist[e.first],e.first));
            }
        }
    }
}
void rebuild(){
    for(int u = 1; u <= n; u++){
        for(auto e : G[u]){
            if(dist[u]+e.second==dist[e.first]){
                reG[u].emplace_back(e.first);
                from[e.first].emplace_back(u);
                degree[e.first]++;
            }
        }
    }
}
void toposort(){
    queue<int> que;
    que.push(s);
    while(!que.empty()){
        int u = que.front();
        que.pop();
        for(int v : reG[u]){
            degree[v]--;
            if(!degree[v]){
                node.push_back(v);
                que.push(v);
            }
        }
    }
}
int LCA(int u, int v){
    if(depth[u]<depth[v]) swap(u,v);
    for(int i = 0; i < 20; i++) if((depth[u]-depth[v])&(1<<i)) u = par[u][i];
    if(u==v) return u;
    for(int i = 19; i >= 0; i--) if(par[u][i]!=par[v][i]){
        u = par[u][i];
        v = par[v][i];
    }
    return par[u][0];
}
void buildtree(){
    for(int u : node){
        if(from[u].empty()) continue;
        par[u][0] = from[u][0];
        for(int v : from[u]) par[u][0] = LCA(par[u][0],v);
        for(int i = 1; par[u][i-1]; i++) par[u][i] = par[par[u][i-1]][i-1];
        GG[par[u][0]].emplace_back(u);
        depth[u] = depth[par[u][0]] + 1;
    }
}
void dfs(int u){
    sz[u] = 1;
    for(int v : GG[u]){
        if(v==par[u][0]) continue;
        dfs(v);
        sz[u] += sz[v];
    }
    if(u!=s) ans = max(ans,sz[u]);
}
void input(){
    scanf("%d %d %d",&n,&m,&s);
    for(int i = 1; i <= m; i++){
        int u,v,c;
        scanf("%d %d %d",&u,&v,&c);
        G[u].emplace_back(make_pair(v,c));
        G[v].emplace_back(make_pair(u,c));
    }
}
int main(){
    input();
    Dijkstra();
    rebuild();
    toposort();
    buildtree();
    dfs(s);
    printf("%d
",ans);
    return 0;
}

2.一般图上的支配树

①洛谷P5180
传送门
模板题,先找出半支配点,然后重新建图,变成DAG支配树,以(1)为根建支配树,答案就是各点子树大小

//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5+7;
int n,m,dfn[MAXN],idx[MAXN],num,depth[MAXN],anc[MAXN],semi[MAXN],root[MAXN],msemi[MAXN],degree[MAXN];
int par[MAXN][20],sz[MAXN];
vector<int> G[MAXN],from[MAXN],newG[MAXN],node,reG[MAXN],refrom[MAXN];
void tarjan(int u){
    dfn[u] = ++num;
    idx[num] = u;
    for(int v : G[u]){
        if(dfn[v]) continue;
        tarjan(v);
        anc[v] = u;
        newG[u].emplace_back(v);
        refrom[v].emplace_back(u);
        degree[v]++;
    }
}
int findroot(int u){
    if(u!=root[u]){
        int rt = root[u];
        root[u] = findroot(root[u]);
        msemi[u] = min(msemi[rt],msemi[u]);
    }
    return root[u];
}
void calsemi(){
    for(int i = 1; i <= n; i++){
        root[i] = i;
        msemi[i] = dfn[i];
    }
    for(int i = n; i >= 2; i--){
        int u = idx[i];
        if(!u) continue;    
        int tar = n;
        for(int v : from[u]){
            if(!dfn[v]) continue;
            if(dfn[v]<dfn[u]) tar = min(tar,dfn[v]);
            else{
                findroot(v);
                tar = min(tar,msemi[v]);
            }
        }
        semi[u] = idx[tar];
        msemi[u] = tar;
        root[u] = anc[u];
        newG[semi[u]].emplace_back(u);
        refrom[u].emplace_back(semi[u]);
        degree[u]++;
    }
}
void toposort(){
    queue<int> que;
    que.push(1);
    while(!que.empty()){
        int u = que.front();
        que.pop();
        for(int v : newG[u]){
            degree[v]--;
            if(degree[v]==0){
                node.emplace_back(v);
                que.push(v);
            }
        }
    }
}
int LCA(int u, int v){
    if(depth[u]<depth[v]) swap(u,v);
    for(int i = 0; depth[u]-depth[v]; i++) if((depth[u]-depth[v])&(1<<i)) u = par[u][i];
    if(u==v) return u;
    for(int i = 19; i >= 0; i--) if(par[u][i]!=par[v][i]){
        u = par[u][i];
        v = par[v][i];
    }
    return par[u][0];
}
void rebuild(){
    for(int u : node){
        if(refrom[u].empty()) continue;
        par[u][0] = refrom[u][0];
        for(int v : refrom[u]) par[u][0] = LCA(par[u][0],v);
        depth[u] = depth[par[u][0]] + 1;
        for(int i = 1; par[u][i-1]; i++) par[u][i] = par[par[u][i-1]][i-1];
        reG[par[u][0]].emplace_back(u);
    }
}
void dfs(int u){
    sz[u] = 1;
    for(int v : reG[u]){
        if(v==par[u][0]) continue;
        dfs(v);
        sz[u] += sz[v];
    }
}
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;
}
void input(){
    n = read(), m = read();
    for(int i = 1; i <= m; i++){
        int u = read(), v = read();
        G[u].emplace_back(v);
        from[v].emplace_back(u);
    }
}
void output(){
    for(int i = 1; i <= n; i++) printf("%d ",sz[i]);
    puts("");
}
int main(){
    input();
    tarjan(1);
    calsemi();
    toposort();
    rebuild();
    dfs(1);
    output();
    return 0;
}

②HDU4694
传送门
上模板,答案就是当前点到根路径上的点标和

//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 5e4+7;
int n,m,num,dfn[MAXN],idx[MAXN],anc[MAXN],root[MAXN],semi[MAXN],msemi[MAXN],degree[MAXN],par[MAXN][20],depth[MAXN],ans[MAXN];
vector<int> G[MAXN],from[MAXN],node,newG[MAXN],rfrom[MAXN],rG[MAXN];
void init(){
    for(int i = 1; i <= n; i++){
        G[i].clear();
        newG[i].clear();
        from[i].clear();
        rfrom[i].clear();
        dfn[i] = idx[i] = degree[i] = 0;
        rG[i].clear();
        memset(par[i],0,sizeof(par[i]));
        ans[i] = 0;
    }
    num = 0;
    node.clear();
}
void tarjan(int u){
    dfn[u] = ++num;
    idx[num] = u;
    for(int v : G[u]){
        if(dfn[v]) continue;
        tarjan(v);
        anc[v] = u;
        newG[u].emplace_back(v);
        rfrom[v].emplace_back(u);
        degree[v]++;
    }
}
int findroot(int u){
    if(u!=root[u]){
        int rt = root[u];
        root[u] = findroot(root[u]);
        msemi[u] = min(msemi[u],msemi[rt]);
    }
    return root[u];
}
void calsemi(){
    for(int i = 1; i <= n; i++){
        root[i] = i;
        msemi[i] = dfn[i];
    }
    for(int i = n; i >= 2; i--){
        if(!idx[i]) continue;
        int u = idx[i];
        int tar = n;
        for(int v : from[u]){
            if(!dfn[v]) continue;
            if(dfn[v]<dfn[u]) tar = min(tar,dfn[v]);
            else{
                findroot(v);
                tar = min(tar,msemi[v]);
            }
        }
        semi[u] = idx[tar];
        msemi[u] = tar;
        root[u] = anc[u];
        newG[semi[u]].emplace_back(u);
        rfrom[u].emplace_back(semi[u]);
        degree[u]++;
    }
}
void toposort(){
    queue<int> que;
    que.push(n);
    while(!que.empty()){
        int u = que.front();
        que.pop();
        for(int v : newG[u]){
            degree[v]--;
            if(!degree[v]){
                node.emplace_back(v);
                que.push(v);
            }
        }
    }
}
int LCA(int u, int v){
    if(depth[u]<depth[v]) swap(u,v);
    for(int i = 0; i < 20; i++) if((depth[u]-depth[v])&(1<<i)) u = par[u][i];
    if(u==v) return u;
    for(int i = 19; i >= 0; i--) if(par[u][i]!=par[v][i]){
        u = par[u][i];
        v = par[v][i];
    }
    return par[u][0];
}
void buildtree(){
    for(int u : node){
        if(rfrom[u].empty()) continue;
        par[u][0] = rfrom[u][0];
        for(int v : rfrom[u]) par[u][0] = LCA(par[u][0],v);
        depth[u] = depth[par[u][0]] + 1;
        rG[par[u][0]].emplace_back(u);
        for(int i = 1; par[u][i-1]; i++) par[u][i] = par[par[u][i-1]][i-1];
    }
}
void dfs(int u, int sum){
    ans[u] = sum;
    for(int v : rG[u]) dfs(v,sum+v);
}
void input(){
    for(int i = 1; i <= m; i++){
        int u,v;
        scanf("%d %d",&u,&v);
        G[u].emplace_back(v);
        from[v].emplace_back(u);
    }
}
void output(){
    for(int i = 1; i <= n; i++) printf(i==n?"%d":"%d ",ans[i]);
    puts("");
}
int main(){
    while(scanf("%d %d",&n,&m)!=EOF){
        init();
        input();
        tarjan(n);
        calsemi();
        toposort();
        buildtree();
        dfs(n,n);
        output();
    }
    return 0;
}

③2014-2015 ACM-ICPC, NEERC, Southern Subregional Contest L useful road
传送门
(1)为根建支配树,检查所有边是不是支配树上的返祖边,如果是,说明必然两次经过祖先节点,路径不是useful road

//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 2e5+7;
int n,m,dfn[MAXN],idx[MAXN],num,degree[MAXN],depth[MAXN],par[MAXN][20],root[MAXN],semi[MAXN],msemi[MAXN];
pair<int,int> edge[MAXN];
vector<int> G[MAXN],from[MAXN],rG[MAXN],rfrom[MAXN],newG[MAXN],node;
void init(){
    for(int i = 1; i <= n; i++){
        G[i].clear();
        from[i].clear();
        rG[i].clear();
        rfrom[i].clear();
        newG[i].clear();
        memset(par[i],0,sizeof(par[i]));
        dfn[i] = idx[i] = degree[i] = 0;
    }
    num = 0;
    node.clear();
}
void tarjan(int u){
    dfn[u] = ++num;
    idx[num] = u;
    for(int v : G[u]){
        if(dfn[v]) continue;
        tarjan(v);
        rG[u].emplace_back(v);
        rfrom[v].emplace_back(u);
        degree[v]++;
    }
}
int findroot(int u){
    if(u!=root[u]){
        int rt = root[u];
        root[u] = findroot(root[u]);
        msemi[u] = min(msemi[u],msemi[rt]);
    }
    return root[u];
}
void calsemi(){
    for(int i = 1; i <= n; i++){
        root[i] = i;
        msemi[i] = dfn[i];
    }
    for(int i = n; i >= 2; i--){
        if(!idx[i]) continue;
        int u = idx[i],tar = n;
        for(int v : from[u]){
            if(!dfn[v]) continue;
            if(dfn[v]<dfn[u]) tar = min(tar,dfn[v]);
            else{
                findroot(v);
                tar = min(tar,msemi[v]);
            }
        }
        semi[u] = idx[tar];
        msemi[u] = tar;
        root[u] = rfrom[u][0];
        rG[semi[u]].emplace_back(u);
        rfrom[u].emplace_back(semi[u]);
        degree[u]++;
    }
}
void toposort(){
    queue<int> que;
    que.push(1);
    while(!que.empty()){
        int u = que.front();
        que.pop();
        for(int v : rG[u]){
            degree[v]--;
            if(!degree[v]){
                node.emplace_back(v);
                que.push(v);
            }
        }
    }
}
int LCA(int u, int v){
    if(depth[u]<depth[v]) swap(u,v);
    for(int i = 0; i < 20; i++) if((depth[u]-depth[v])&(1<<i)) u = par[u][i];
    if(u==v) return u;
    for(int i = 19; i >= 0; i--) if(par[u][i]!=par[v][i]){
        u = par[u][i];
        v = par[v][i];
    }
    return par[u][0];
}
void buildtree(){
    for(int u : node){
        if(rfrom[u].empty()) continue;
        par[u][0] = rfrom[u][0];
        for(int v : rfrom[u]) par[u][0] = LCA(par[u][0],v);
        depth[u] = depth[par[u][0]] + 1;
        newG[par[u][0]].emplace_back(u);
        for(int i = 1; par[u][i-1]; i++) par[u][i] = par[par[u][i-1]][i-1];
    }
}
int main(){
    while(scanf("%d %d",&n,&m)!=EOF){
        init();
        for(int i = 1; i <= m; i++){
            scanf("%d %d",&edge[i].first,&edge[i].second);
            from[edge[i].second].emplace_back(edge[i].first);
            G[edge[i].first].emplace_back(edge[i].second);
        }
        tarjan(1);
        calsemi();
        toposort();
        buildtree();
        vector<int> vec;
        for(int i = 1; i <= m; i++){
            if(!dfn[edge[i].first]||!dfn[edge[i].second]) continue;
            int lca = LCA(edge[i].first,edge[i].second);
            if(lca!=edge[i].second) vec.emplace_back(i);
        }
        printf("%d
",vec.size());
        for(int x : vec) printf("%d ",x);
        puts("");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/kikokiko/p/12210099.html