2022“杭电杯”中国大学生算法设计超级联赛(10)部分题题解
Minimum Diameter
趁着这个题,得赶紧把关于直径的知识点整理一下。
众所周知,直径有两种求法,一种是DP的方法,一种是两遍bfs/dfs。
对于直径以下知识点需要知道:
若干条直径一定经过中点。两棵树合并成一棵树,新的直径一定也是原来四个端点中的两个。
这个题,考虑离线边长,之后的合并两个连通块时,新的直径考虑枚举四个点求他们的距离,发现他们的lca一定是这两个树中的点,所以用离线处理的信息就能解决。
点击查看代码
#include<bits/stdc++.h>
#define db double
using namespace std;
const int N=1e5+10;
int n,m,T,U[N],V[N],vis[N],deep[N],f[N][25];
int a[N],b[N],fa[N],cnt[N],px,c[N];
//a,b表示每个连通块的两个直径端点.
vector<int>son[N];
inline int getf(int x){return fa[x]==x?x:fa[x]=getf(fa[x]);}
inline void dfs(int x,int fa)
{
for(auto y:son[x])
{
if(y==fa) continue;
vis[y]=1;
deep[y]=deep[x]+1;
f[y][0]=x;
for(int i=1;i<=20;++i) f[y][i]=f[f[y][i-1]][i-1];
dfs(y,x);
}
}
inline int Lca(int a,int b)
{
if(deep[a]>deep[b]) swap(a,b);
for(int i=20;i>=0;--i)
if(deep[f[b][i]]>=deep[a]) b=f[b][i];
if(a==b) return a;
for(int i=20;i>=0;--i)
{
if(f[a][i]!=f[b][i])
a=f[a][i],b=f[b][i];
}
return f[a][0];
}
inline int dis(int a,int b)
{
return deep[a]+deep[b]-2*deep[Lca(a,b)];
}
inline void merge(int x,int y)
{
fa[y]=x;
int d[4]={a[x],b[x],a[y],b[y]};
cnt[c[x]]--;cnt[c[y]]--;
for(int i=0;i<4;++i)
for(int j=i+1;j<4;++j)
{
int ds=dis(d[i],d[j]);
if(ds>c[x])
{
a[x]=d[i];b[x]=d[j];
c[x]=ds;
}
}
cnt[c[x]]++;
px=max(px,c[x]);
}
inline void solve()
{
cin>>n>>m;
for(int i=1;i<=n;++i)
{
vis[i]=0;deep[i]=0;
son[i].clear();c[i]=0;
for(int j=0;j<=20;++j) f[i][j]=0;
cnt[i]=0;
}
cnt[0]=n;px=0;
for(int i=1;i<=m;++i)
{
cin>>U[i]>>V[i];
son[U[i]].push_back(V[i]);
son[V[i]].push_back(U[i]);
}
for(int i=1;i<=n;++i) if(!vis[i])
{
vis[i]=1;
dfs(i,0);
}
for(int i=1;i<=n;++i) fa[i]=i,a[i]=i,b[i]=i;
for(int i=1;i<=m;++i)
{
int t1=getf(U[i]),t2=getf(V[i]);//找到需要合并的两个连通块.
merge(t1,t2);
int d[4]={-1,-1,-1,-1},o=0;
for(int j=px;j>=max(0,px-5);--j)
{
for(int k=1;k<=cnt[j];++k)
{
d[++o]=j;
if(o==3) break;
}
if(o==3) break;
}
int ans=d[1];
if(d[2]!=-1) ans=max(ans,(int)ceil(1.0*d[1]/2)+(int)ceil(1.0*d[2]/2)+1);
if(d[3]!=-1) ans=max(ans,(int)ceil(1.0*d[2]/2)+(int)ceil(1.0*d[3]/2)+2);
cout<<ans<<endl;
}
}
int main()
{
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>T;
while(T--) solve();
return 0;
}
Photos
留坑待填...