BZOJ4304 : 道路改建
首先求出SCC,把图缩点成一个DAG。
通过拓扑排序+DP求出:
dp0[x]:从x点出发能到的点的集合。
dp1[x]:能到x的点的集合。
对于一条边x->y,将它改为双向边后,形成的新的SCC的点数为dp0[x]&dp1[y]中1的个数,用bitset维护。
时间复杂度$O(\frac{n^3}{32})$。
#include<cstdio> #include<bitset> using namespace std; const int N=2002,M=N*N; int n,m,i,j,x,y,v[N],q[M],h,t,d[N],f[N],ans,res[M]; int g[N][N];bool G[N][N],vis[M]; bitset<N>dp0[N],dp1[N]; inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';} void dfs1(int x){ v[x]=1; for(int i=1;i<=n;i++)if(g[x][i]&&!v[i])dfs1(i); q[++t]=x; } void dfs2(int x,int y){ v[x]=0,f[x]=y;dp0[y][x]=dp1[y][x]=1; for(int i=1;i<=n;i++)if(g[i][x]&&v[i])dfs2(i,y); } int main(){ read(n),read(m); for(i=1;i<=m;i++)read(x),read(y),g[x][y]=i; for(i=1;i<=n;i++)if(!v[i])dfs1(i); for(i=n;i;i--)if(v[q[i]])dfs2(q[i],q[i]); for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(g[i][j]&&f[i]!=f[j])if(!G[f[i]][f[j]])G[f[i]][f[j]]=1,d[f[j]]++; for(i=h=1,t=0;i<=n;i++)if(f[i]==i&&!d[i])q[++t]=i; while(h<=t)for(x=q[h++],i=1;i<=n;i++)if(G[x][i]){ dp1[i]|=dp1[x]; if(!(--d[i]))q[++t]=i; } for(i=t;i;i--)for(x=q[i],j=1;j<=n;j++)if(G[j][x])dp0[j]|=dp0[x]; for(t=0,i=1;i<=n;i++)for(j=1;j<=n;j++)if(g[i][j]){ int now=(dp0[f[i]]&dp1[f[j]]).count(); if(now<2)continue; if(now>ans)ans=now,q[t=1]=g[i][j];else if(now==ans)q[++t]=g[i][j]; } printf("%d\n%d\n",ans,t); for(i=1;i<=t;i++)vis[q[i]]=1; for(t=0,i=1;i<=m;i++)if(vis[i])res[++t]=i; if(t)for(printf("%d",res[1]),i=2;i<=t;i++)printf(" %d",res[i]); return 0; }