• 支配树题集


    $$支配树题集$$

    巨佬证明
    搭配着看

    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;
    }
    
  • 相关阅读:
    DShow实现一个avi视频的播放(含有个人解释和注释)
    指针和引用的区别
    从头看算法导论 习题2.3-7 深入分析
    程序员福利各大平台免费接口,非常适用
    NYOJ 58 最少步数
    NYOJ 42 一笔画问题
    NYOJ 1058 部分和问题
    NYOJ 21 三个水杯
    winform里面网页显示指定内容
    C#中List<T>对象的深度拷贝问题
  • 原文地址:https://www.cnblogs.com/kikokiko/p/12210099.html
Copyright © 2020-2023  润新知