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];
    }
}
posted @ 2026-10-13 20:18  tidewave  阅读(5)  评论(0)    收藏  举报