[JZOJ3486]道路改建

Posted skylee的OI博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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\n%llu\n",ans,vec.size());
    for(register unsigned i=0;i<vec.size();i++) {
        printf("%d ",vec[i]);
    }
    return 0;
}

 

以上是关于[JZOJ3486]道路改建的主要内容,如果未能解决你的问题,请参考以下文章

JZOJ 3521. 道路覆盖

[JZOJ5465]道路重建--边双缩点+树的直径

北京道路命名

[数论][LCA][并查集]JZOJ 5782 城市猎人

公路村村通

7-10 公路村村通