[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\n%llu\n",ans,vec.size()); for(register unsigned i=0;i<vec.size();i++) { printf("%d ",vec[i]); } return 0; }