更新日誌
概念
圖上最小點定連通塊,就是給出無向連通圖上一些點,要求找出邊權和最小的連通分量使這些點強連通。
現在要求這個連通塊內的邊權之和。
思路
先給出結論:把節點按照dfs序排序,統計所有相鄰的節點以及起始點與末尾點之間的距離,將它們求和,所求的答案即為這個和除以2。
感性證明一下,可以想象一個人(或者螞蟻,或者飛船,或者任何能沿著路走的生物,或者不是生物也行),在dfs生成樹上走上、走下,依次走向每一個節點,最後走會原點,不難想象到每一條邊都被他走了兩遍。
稍微理性一點的話,因為是按照dfs序依次排列的,就有幾種情況:
- 一點是另一點的祖先節點,直接走下去。
- 二者屬於不同子樹,先走回到公共祖先節點(必然是之前已經經過的節點),再走進一條新的路徑
重複二個操作,那麼都是先走到最深的節點,再回溯到某個點,再沿著另一個路徑搜尋……最後回到起始點,每條邊就都被走了兩次。
例題
CF372D
程式碼
前注:非題解,不做詳細講解
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,T=32;
int n,k;
int ans;
int distal;
vector<int> vs[N];
set<int> dst;
int dp[N];
int f[N][T+5];
void brinit(){
for(int t=1;t<=T;t++){
for(int i=1;i<=n;i++){
f[i][t]=f[f[i][t-1]][t-1];
}
}
}
int lca(int a,int b){
if(dp[a]>dp[b])swap(a,b);
for(int t=T;t>=0;t--){
if(dp[f[b][t]]>=dp[a])b=f[b][t];
}
if(a==b)return a;
for(int t=T;t>=0;t--){
if(f[a][t]!=f[b][t]){
a=f[a][t];
b=f[b][t];
}
}
return f[a][0];
}
int dis(int a,int b){
int c=lca(a,b);
return dp[a]-dp[c]+dp[b]-dp[c];
}
int cnt;
int dfn[N];
void dfs(int now,int fa){
dfn[now]=++cnt;
f[dfn[now]][0]=dfn[fa];
dp[dfn[now]]=dp[dfn[fa]]+1;
for(auto nxt:vs[now]){
if(nxt==fa)continue;
dfs(nxt,now);
}
}
void pushin(int x){
dst.insert(dfn[x]);
int now,lst,nxt;
now=lst=nxt=0;
auto plc=dst.lower_bound(dfn[x]);
now=*plc;
auto lstp=plc;
if(lstp==dst.begin())lst=*dst.rbegin();
else{advance(lstp,-1);lst=*lstp;}
auto nxtp=plc;advance(nxtp,1);
if(nxtp==dst.end())nxt=*dst.begin();
else nxt=*nxtp;
distal-=dis(lst,nxt);
distal+=dis(lst,now);
distal+=dis(now,nxt);
}
void throut(int x){
int now,lst,nxt;
now=lst=nxt=0;
auto plc=dst.lower_bound(dfn[x]);
now=*plc;
auto lstp=plc;
if(lstp==dst.begin())lst=*dst.rbegin();
else{advance(lstp,-1);lst=*lstp;}
auto nxtp=plc;advance(nxtp,1);
if(nxtp==dst.end())nxt=*dst.begin();
else nxt=*nxtp;
distal+=dis(lst,nxt);
distal-=dis(lst,now);
distal-=dis(now,nxt);
dst.erase(dfn[x]);
}
inline bool check(){
return distal/2+1<=k;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>k;
int a,b;
for(int i=1;i<n;i++){
cin>>a>>b;
vs[a].push_back(b);
vs[b].push_back(a);
}
dfs(1,1);
brinit();
for(int i=0,j=1;i<=n;pushin(++i)){
while(!check()){
throut(j++);
}
ans=max(ans,i-j+1);
}
cout<<ans;
return 0;
}