题目大意:
给你一个有向图,你可以把其中某一条单向边改成双向边,使得图中最大的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^'';
while(isdigit(ch=getchar())) x=(((x<<)+x)<<)+(ch^'');
return x;
}
const int N=,M=;
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=;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=;
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=;i<=id;i++) {
if(!deg[i]) {
q.push(i);
}
}
while(!q.empty()) {
const int x=q.front();
q.pop();
for(register unsigned i=;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=;i<m;i++) {
edge[i]=(Edge){getint(),getint()};
add_edge(edge[i].u,edge[i].v);
}
for(register int i=;i<=n;i++) {
if(!dfn[i]) {
tarjan(i);
}
e[i].clear();
}
for(register int i=;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=;
static std::vector<int> vec;
for(register int i=;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+);
} else if((out[u]&in[v]).count()==ans) {
vec.push_back(i+);
}
}
printf("%u\n%llu\n",ans,vec.size());
for(register unsigned i=;i<vec.size();i++) {
printf("%d ",vec[i]);
}
return ;
}