CF516D Drazil and Morning Exercise

\(\texttt{link}\)

首先容易计算出每个点的 \(f\)。

一种暴力的做法是每次将两端的 \(f\) 值的差 \(\le lim\) 的边存下来排序,双指针+\(LCT\)(线段树分治),但是常数很大,过不了。

巧妙的做法是以 \(f\) 值最小的点为根建树,这个点必然在直径上,并且每个节点的 \(f\) 都不会小于父亲节点的 \(f\),于是就可以对于每个点二分一个最浅的祖先,那么以 祖先 \(\rightarrow\) 当前点 路径上的节点的 \(f\) 为最小值时的答案就 \(+1\),这个可以差分,然后就做完了。

Ps: 求 \(f\) 可以不用换根 \(dp\),直接找直径端点求就行((

\(\texttt{Code:}\)

#include<bits/stdc++.h>
#define mp make_pair
#define fi first
#define se second
#define ll long long
using namespace std;
const int maxn=1e5+5;
struct Edge{int to,dis,nxt;}E[maxn<<1];
int cnt,head[maxn];
void addedg(int u,int v,int w){E[++cnt]=(Edge){v,w,head[u]};head[u]=cnt;}
int n,df[maxn],sn[maxn];
ll f[maxn][3],g[maxn];
void dfs1(int u,int fa){
    for(int i=head[u];i;i=E[i].nxt){
        int v=E[i].to;
        if(v==fa)continue;
        df[v]=E[i].dis;dfs1(v,u);
        if(f[v][0]+E[i].dis>f[u][0]){
            sn[u]=v;f[u][1]=f[u][0];f[u][0]=f[v][0]+E[i].dis;
        }else f[u][1]=max(f[u][1],f[v][0]+E[i].dis);
    }
}
void dfs2(int u,int fa){
    if(fa){
        if(sn[fa]==u)f[u][2]=max(f[fa][1],f[fa][2])+df[u];
            else f[u][2]=max(f[fa][0],f[fa][2])+df[u];
    }
    for(int i=head[u];i;i=E[i].nxt){
        int v=E[i].to;
        if(v==fa)continue;
        dfs2(v,u);
    }
}
int sb[maxn],fa[maxn],tag[maxn],ans;
void dfs(int u,int fath,int depth,ll lim){
    sb[depth]=u;fa[u]=fath;
    int l=0,r=depth;
    while(l+1<r){
        int mid=(l+r)>>1;
        if(g[u]-g[sb[mid]]<=lim)r=mid;else l=mid;
    }tag[u]++;tag[fa[sb[r]]]--;
    for(int i=head[u];i;i=E[i].nxt){
        int v=E[i].to;
        if(v==fath)continue;
        dfs(v,u,depth+1,lim);
    }
}
void query(int u){
    for(int i=head[u];i;i=E[i].nxt){
        int v=E[i].to;
        if(v==fa[u])continue;
        query(v);tag[u]+=tag[v];
    }ans=max(ans,tag[u]);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int u,v,w;scanf("%d%d%d",&u,&v,&w);
        addedg(u,v,w);addedg(v,u,w);
    }dfs1(1,0);dfs2(1,0);
    for(int i=1;i<=n;i++)g[i]=max(f[i][0],f[i][2]);
    int rt=0;g[0]=1e12;
    for(int i=1;i<=n;i++)if(g[i]<g[rt])rt=i;
    int q;scanf("%d",&q);
    while(q--){
        ll lim;scanf("%lld",&lim);
        for(int i=1;i<=n;i++)tag[i]=0;
        dfs(rt,0,1,lim);
        ans=0;query(rt);
        printf("%d\n",ans);
    }
    return 0;
}

上一篇:力扣-三数之和


下一篇:【LeetCode 21】28. 实现 strStr()