题解:P16918 [JLCPC 2026] 图
lailai0916 · · 题解
题意简述
给定一张简单无向连通图。选择两条不同的边删除,并要求剩余图仍然连通。
对于每条被删边,计算它的两个端点在剩余图中的最短路。求两段最短路长度之和的最小值,以及达到最小值的无序删边方案数。
解题思路
对边
只删除
若同时删除不同的边
要取到这个下界,
先确定答案的最小值。设全图最短环长度为
任取一组合法删边方案。两条被删边在剩余图中的最短路,分别与原边构成两个环。第一个环包含
再取长度分别为
删除
还需从所有
若
下面统计达到最小值的删边方案。对每条非桥边
考虑另一条边
所有满足条件的边组成最短路图。沿任意最短路前进时,
同时,满足距离等式的每条边都至少属于一条最短路。因此,边
用位集
并且
此时剩余图也一定连通。因为两条被删边的端点都已经在剩余图中连通,把它们加回去不会合并任何连通块;而全部加回后是原连通图,所以剩余图只能有一个连通块。
每条边需要两次 BFS 和两次边扫描。时间复杂度为
参考代码
#include <bits/stdc++.h>
using namespace std;
using pii=pair<int,int>;
const int N=5005;
const int M=5005;
const int inf=0x3f3f3f3f;
struct Edge
{
int u,v;
}e[M];
int n,m;
int d[2][N],cyc[M],lev[M],cnt[N];
vector<pii> g[N];
bitset<M> must[M];
void bfs(int s,int ban,int o)
{
fill(d[o]+1,d[o]+n+1,-1);
queue<int> q;
d[o][s]=0;
q.push(s);
while(!q.empty())
{
int x=q.front();
q.pop();
for(auto [y,id]:g[x])
{
if(id==ban||d[o][y]!=-1)continue;
d[o][y]=d[o][x]+1;
q.push(y);
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin>>t;
while(t--)
{
cin>>n>>m;
for(int i=1;i<=n;i++)g[i].clear();
for(int i=0;i<m;i++)
{
cin>>e[i].u>>e[i].v;
g[e[i].u].push_back({e[i].v,i});
g[e[i].v].push_back({e[i].u,i});
must[i].reset();
}
for(int i=0;i<m;i++)
{
bfs(e[i].u,i,0);
if(d[0][e[i].v]==-1)
{
cyc[i]=inf;
continue;
}
bfs(e[i].v,i,1);
cyc[i]=d[0][e[i].v]+1;
fill(cnt,cnt+n,0);
fill(lev,lev+m,-1);
for(int j=0;j<m;j++)
{
if(j==i)continue;
int x=e[j].u,y=e[j].v;
if(d[0][x]+1+d[1][y]==cyc[i]-1)lev[j]=d[0][x];
else if(d[0][y]+1+d[1][x]==cyc[i]-1)lev[j]=d[0][y];
if(lev[j]!=-1)cnt[lev[j]]++;
}
for(int j=0;j<m;j++)
{
if(lev[j]!=-1&&cnt[lev[j]]==1)must[i][j]=1;
}
}
int mn=inf;
for(int i=0;i<m;i++)mn=min(mn,cyc[i]);
int num=0,nxt=inf;
for(int i=0;i<m;i++)
{
if(cyc[i]==mn)num++;
else if(cyc[i]>mn)nxt=min(nxt,cyc[i]);
}
if(num>mn)nxt=mn;
int sum=mn+nxt-2,ans=0;
for(int i=0;i<m;i++)
{
for(int j=i+1;j<m;j++)
{
if(cyc[i]+cyc[j]-2==sum&&!must[i][j]&&!must[j][i])ans++;
}
}
cout<<sum<<' '<<ans<<'\n';
}
return 0;
}