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
%d
",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;
}

  

原文地址:https://www.cnblogs.com/clrs97/p/4915176.html