正题
题目链接:https://www.luogu.com.cn/problem/CF516D
题目大意
给出一棵nnn个点的树,定义f(x)f(x)f(x)表示距离点xxx最远的点的距离,qqq次询问给出一个kkk,要求一个最大的连通块满足连通块中所有点的f(x)f(x)f(x)最大最小差值不能超过kkk。
1≤n≤105,1≤q≤501\leq n\leq 10^5,1\leq q\leq 501≤n≤105,1≤q≤50
解题思路
我们找到f(x)f(x)f(x)最小的点作为根,那么肯定有每一个点的f(x)f(x)f(x)都不小于其父节点的,具体原理是因为每个点出发的最长简单路端点肯定是直径的某一端,而这个最小的f(x)f(x)f(x)可以视为直径的中点。
那么对于每个询问我们就直接枚举所有点,然后往上倍增到一个深度最浅的祖先满足f(x)−f(z)≤kf(x)-f(z)\leq kf(x)−f(z)≤k,然后用树上差分给z↔xz\leftrightarrow xz↔x路径上所有点的权值+1+1+1,最后求一个点权最大的点就好了。
时间复杂度:O(qnlogn)O(qn\log n)O(qnlogn)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1e5+10;
struct node{ll to,next,w;
}a[N<<1];
ll n,q,tot,rt,ls[N],len[N];
ll f[N][18],dep[N],c[N];
void addl(ll x,ll y,ll w){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;return;
}
void dfs(ll x,ll fa){for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa)continue;dfs(y,x);len[x]=max(len[x],len[y]+a[i].w);}return;
}
void calc(ll x,ll fa,ll mxl){ll mx=mxl,mi=0;len[x]=max(len[x],mxl);for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa)continue;if(mx<len[y]+a[i].w)mi=mx,mx=len[y]+a[i].w;else if(mi<len[y]+a[i].w)mi=len[y]+a[i].w;}for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa)continue;if(mx==len[y]+a[i].w)calc(y,x,mi+a[i].w);else calc(y,x,mx+a[i].w);}return;
}
void sfd(ll x,ll fa){dep[x]=dep[fa]+1;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa)continue;f[y][0]=x;sfd(y,x);}return;
}
void carc(ll x,ll fa){for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa)continue;carc(y,x);c[x]+=c[y];}return;
}
void Query(ll d){memset(c,0,sizeof(c));len[0]=-1e18;for(ll i=1;i<=n;i++){ll x=i;for(ll j=17;j>=0;j--)if(len[i]-len[f[x][j]]<=d)x=f[x][j];c[i]++;c[f[x][0]]--;}carc(rt,0);ll ans=0;for(ll i=1;i<=n;i++)ans=max(ans,c[i]);printf("%lld\n",ans);return;
}
signed main()
{scanf("%lld",&n);for(ll i=1,x,y,w;i<n;i++){scanf("%lld%lld%lld",&x,&y,&w);addl(x,y,w);addl(y,x,w);}dfs(1,0);calc(1,0,0);len[0]=1e18;for(ll i=1;i<=n;i++)if(len[i]<len[rt])rt=i;sfd(rt,0);for(int j=1;j<18;j++)for(int i=1;i<=n;i++)f[i][j]=f[f[i][j-1]][j-1];scanf("%lld",&q);while(q--){ll x;scanf("%lld",&x);Query(x);}return 0;
}