[CF475E] Strongly Connected City 2 题解
首先对于一个边双连通分量,它一定有一个环,将这个无向环换成有向环,我们就构造出了一种可以使边双连通分量内任意两点都可互达的情况。
那么问题又一次来到了树的情况。注意力惊人的注意到最优策略一定是一堆点到一个点,一个点再到一堆点。直接简单树形 \(dp\) 结合简单 \(01\) 背包即可。
时间复杂度 \(O(n^2+m)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=2005,M=2e6+5;
int n,m,tp,id,idx[N],vis[N],u[M],f[N];
int st[N],cnt,dfn[N],low[N],v[M],sm[N];
vector<int>g[N],ve[N];int ans;
void tarjan(int x,int fa){
dfn[x]=low[x]=++id,vis[st[++tp]=x]=1;
for(auto y:g[x]){
if(!dfn[y]) tarjan(y,x),low[x]=min(low[x],low[y]);
else if(y!=fa&&vis[y]) low[x]=min(low[x],dfn[y]);
}if(dfn[x]!=low[x]) return;cnt++;
while(st[tp+1]!=x)
vis[st[tp]]=0,idx[st[tp--]]=cnt,sm[cnt]++;
}pair<int,int>dfs(int x,int fa){
int siz=sm[x],dp=0;pair<int,int>nw;
for(auto y:ve[x]) if(y!=fa)
nw=dfs(y,x),siz+=nw.first,dp+=nw.second;
return {siz,dp+siz*sm[x]};
}int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>u[i]>>v[i];
g[u[i]].push_back(v[i]);
g[v[i]].push_back(u[i]);
}for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i,0);
for(int i=1;i<=m;i++){
if(idx[u[i]]==idx[v[i]]) continue;
ve[idx[u[i]]].push_back(idx[v[i]]);
ve[idx[v[i]]].push_back(idx[u[i]]);
}for(int i=1;i<=cnt;i++){
for(int j=1;j<=n;j++) f[j]=0;
int sum=0,sz=0;f[0]=1;
for(auto j:ve[i]){
pair<int,int>nw=dfs(j,i);
sum+=nw.second,sz=nw.first;
for(int k=n;k>=sz;k--) f[k]|=f[k-sz];
}for(int j=0;j<=n;j++)
if(f[j]) ans=max(ans,sum+(j+sm[i])*(n-j));
}cout<<ans;
return 0;
}