【CF思维训练】CF1804F Approximate Diameter
Preface
十分有趣的一道思维题。
Problem
给你一个
其中
数据范围:
Solution
观察这道题,发现这个输出模糊值的设定十分有趣。
你发现,如果想求出一个无向图的具体直径是
但是你注意到,你设
这和模糊值的定义式非常相似。
关于证明,对于直径上的
继续探究,你发现对于每一次加边,对于任意
你只需要把 BFS 搜索树建出来,这个操作实际上等于移植一棵子树,这最多会导致
你把所有的操作离线下来,由于模糊值的定义式允许你小于等于直径的二倍,那么你自然而然地想到,你一次算出的
直径太不好处理了,我们干脆把他也变成
这个太好做了,我们直接可以二分跳到哪个节点。
然后你发现,由于每一次跳跃会使答案除二,你最多会跳跃
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=2e5+10;
int n,m,q,u[N],v[N],dep[N];
vector <int> edge[N];
queue <int> Q;
int BFS(){int res=0;
for(int i=1;i<=n;i++)
dep[i]=1e9;
Q.push(1);dep[1]=0;
while(Q.size()){
int u=Q.front();
for(auto v:edge[u]){
if(dep[v]>dep[u]+1)
dep[v]=dep[u]+1,Q.push(v);
}
if(Q.size()==1)
res=dep[u];
Q.pop();
}return res;
}
int check(int x){
for(int i=1;i<=x;i++)
edge[u[i]].pb(v[i]),edge[v[i]].pb(u[i]);
int tmp=BFS();//cout<<"debug: "<<x<<" "<<tmp<<endl;
for(int i=x;i>=1;i--)
edge[u[i]].pop_back(),edge[v[i]].pop_back();
return tmp;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n>>m>>q;
for(int i=1;i<=m;i++){
cin>>u[0]>>v[0];
edge[u[0]].pb(v[0]),edge[v[0]].pb(u[0]);
}for(int i=1;i<=q;i++)cin>>u[i]>>v[i];
for(int i=0;i<=q;i++){
int now=check(i);
int l=i,r=q;
while(l<r){
int mid=(l+r+1)/2;
if(check(mid)*2<now)
r=mid-1;
else l=mid;
}
for(int j=i;j<=l;j++)cout<<now<<" ";
i=l;
}
return 0;
}