CF1804F题解
Approximate Diameter
非常神的题目啊,记录一下。
题目传送门
总结
像这种近似化的题目,我们可以发现它的答案很多时候是没必要修改的,于是我们可以通过一些方法(如二分)找修改点,这样可以优化不少
题目大意
给你一个无向联通图,每次加一条边,求每次的直径,答案范围不能正确答案相差一倍(你的答案 \(ans\) 与正确答案 \(Ans\) 满足 $ \left\lceil \frac{Ans}{2} \right\rceil \le ans \le 2Ans $)
题目分析
首先从题目性质入手,答案范围为 $ \left\lceil \frac{Ans}{2} \right\rceil \le ans \le 2Ans $,发现图的直径大小 $ s $ 与任意点到另一点的距离 \(t\) 也有类似形式:$ \left\lceil \frac{s}{2} \right\rceil \le t \le s $,于是不难想到枚举答案只有到 \(\left\lceil \frac{s}{2} \right\rceil\) 以下才需要修改,二分即可。
注:其实因为任意一个点都满足 $ \left\lceil \frac{s}{2} \right\rceil \le t \le s $,所以我们可以把直径当成任意钦定的一个点起始的最长路。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,q,s[N],dis[N],vis[N];
struct edge
{
int v,ti;
};
vector<edge>e[N];
int get(int st,int t)
{
if(s[t])return s[t];
for(int i=1;i<=n;i++)vis[i]=0;
vis[1]=1;dis[1]=0;//随便找一个点都满足范围,直接充当直径
int ans=0;
queue<int>q;
q.push(1);
while(!q.empty())
{
int x=q.front();q.pop();
for(edge to:e[x])
{
int y=to.v;
if(to.ti>t||vis[y])continue;
vis[y]=1;
dis[y]=dis[x]+1;
ans=max(ans,dis[y]);
q.push(y);
}
}
return s[t]=ans;
}
int main()
{
cin>>n>>m>>q;
for(int i=1;i<=m;i++)
{
int x,y;
cin>>x>>y;
e[x].push_back({y,0});
e[y].push_back({x,0});
}
for(int i=1;i<=q;i++)
{
int x,y;
cin>>x>>y;
e[x].push_back({y,i});
e[y].push_back({x,i});
}
int fi=get(1,0),now=1;
cout<<fi<<' ';
while(now<=q)
{
int l=now,r=q;
while(l<=r)
{
int mid=(l+r)/2;
if(get(1,mid)*2>=fi)l=mid+1;//二分需要更改才改
else r=mid-1;
}
for(int i=now;i<=r;i++)cout<<fi<<' ';
now=r+1;fi=s[now];
}
}

浙公网安备 33010602011771号