zoukankan      html  css  js  c++  java
  • [BZOJ4304]/[JZOJ3486]道路改建

    题目大意:
      给你一个有向图,你可以把其中某一条单向边改成双向边,使得图中最大的SCC最大。
      问SCC最大能是多少,有哪些方案?

    思路:
      对原图缩点后就变成了一个DAG。
      我们在DAG上DP,记录一下从点i出发能到达的点集out[i],以及能到达i的点的集合in[i]。
      最后枚举每一条边(u->v),将它改为双向边就相当于将所有u,v之间的点都连通起来,也就是求out[u]和in[v]的交。
      最后我们看一下哪个交最大,以及这么大的有哪些边即可。
      注意要用bitset优化,不然只有60分。

    #include<stack>
    #include<queue>
    #include<cstdio>
    #include<cctype>
    #include<bitset>
    #include<vector>
    inline int getint() {
        register char ch;
        while(!isdigit(ch=getchar()));
        register int x=ch^'0';
        while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
        return x; 
    }
    const int N=2001,M=4000000;
    struct Edge {
        int u,v;
    };
    Edge edge[M];
    std::vector<int> e[N],e2[N];
    inline void add_edge(const int &u,const int &v) {
        e[u].push_back(v);
    }
    int ind[N],outd[N];
    std::bitset<N> in[N],out[N];
    int dfn[N],low[N],scc[N],cnt,id;
    std::stack<int> s;
    bool ins[N];
    void tarjan(const int &x) {
        dfn[x]=low[x]=++cnt;
        s.push(x);
        ins[x]=true;
        for(register unsigned i=0;i<e[x].size();i++) {
            const int &y=e[x][i];
            if(!dfn[y]) {
                tarjan(y);
                low[x]=std::min(low[x],low[y]);
            } else if(ins[y]) {
                low[x]=std::min(low[x],dfn[y]);
            }
        }
        if(dfn[x]==low[x]) {
            id++;
            int y=0;
            while(y!=x) {
                y=s.top();
                s.pop();
                ins[y]=false;
                scc[y]=id;
                in[id].set(y);
                out[id].set(y);
            }
        }
    }
    inline void kahn(const std::vector<int> e[],int deg[],std::bitset<N> set[]) {
        static std::queue<int> q;
        for(register int i=1;i<=id;i++) {
            if(!deg[i]) {
                q.push(i);
            }
        }
        while(!q.empty()) {
            const int x=q.front();
            q.pop();
            for(register unsigned i=0;i<e[x].size();i++) {
                const int &y=e[x][i];
                set[y]|=set[x];
                if(!--deg[y]) {
                    q.push(y);
                }
            }
        }
    }
    int main() {
        int n=getint(),m=getint();
        for(register int i=0;i<m;i++) {
            edge[i]=(Edge){getint(),getint()};
            add_edge(edge[i].u,edge[i].v);
        }
        for(register int i=1;i<=n;i++) {
            if(!dfn[i]) {
                tarjan(i);
            }
            e[i].clear();
        }
        for(register int i=0;i<m;i++) {
            const int &u=scc[edge[i].u],&v=scc[edge[i].v];
            if(u==v) continue;
            e[u].push_back(v);
            e2[v].push_back(u);
            outd[u]++,ind[v]++;
        }
        kahn(e,ind,in);
        kahn(e2,outd,out);
        unsigned ans=0;
        static std::vector<int> vec;
        for(register int i=0;i<m;i++) {
            const int &u=scc[edge[i].u],&v=scc[edge[i].v];
            if((out[u]&in[v]).count()>ans) {
                ans=(out[u]&in[v]).count();
                vec.clear();
                vec.push_back(i+1);
            } else if((out[u]&in[v]).count()==ans) {
                vec.push_back(i+1);
            }
        }
        printf("%u
    %llu
    ",ans,vec.size());
        for(register unsigned i=0;i<vec.size();i++) {
            printf("%d ",vec[i]);
        }
        return 0;
    }
  • 相关阅读:
    Linux基础-yum软件包管理
    Linux基础-rpm软件包管理
    Linux基础-简单的进程操作
    Linux基础-free窥内存-dd探硬盘
    Linux基础-swap交换分区
    Linux基础-软硬连接Block概念
    Linux基础操作-分区概念
    Linux基础-vim编辑器
    Linux基础操作命令-打包压缩
    RandomAccessFile 文件读写中文乱码解决方案!
  • 原文地址:https://www.cnblogs.com/skylee03/p/7765548.html
Copyright © 2011-2022 走看看