• [cf516D]Drazil and Morning Exercise


    令$f_{k}$为离$k$最远的点到$k$的距离,任取树的一条直径$(x,y)$,有$f_{k}=max(dis(k,x),dis(k,y))$

    更进一步的,取直径中点$mid$(这里定义为$f_{mid}$最小的点,有多个任取一点)并以其为根建树,则所有节点儿子的$f$不小于父亲的$f$

    根据这个性质,枚举这个连通块的根$k$(也可以说是连通块内$f$最小的点),即统计其子树内权值在$[f_{k},f_{k}+l]$中的点数量,但可持久化线段树去维护会TLE

    事实上,这个还有一种比较巧妙地方式计算:

    将所有$f$从大到小排序,依次枚举$k$,去计算$k$子树内权值在$[f_{k},f_{k}+l]$的点

    通过单调性,在左边删除、右边加入可以维护出在排序后的对应区间,现在将这个区间内的所有点的导出子图用并查集去维护,那么所求的也就是$k$子树大小

    关于如何维护并查集,也就是要支持插入和删除,插入将其树上所有儿子都与其合并,删除的节点必然是导出子图上的叶子,直接删除即可

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define N 100005
      4 #define ll long long
      5 #define fi first
      6 #define se second
      7 struct Edge{
      8     int nex,to,len;
      9 }edge[N<<1];
     10 int V,E,n,m,x,y,z,head[N],sh[N],f[N][21],fa[N],sz[N];
     11 ll l,dep[N];
     12 pair<ll,int>ans[N];
     13 int find(int k){
     14     if (k==fa[k])return k;
     15     return fa[k]=find(fa[k]);
     16 }
     17 void merge(int x,int y){
     18     x=find(x),y=find(y);
     19     if (x!=y){
     20         fa[x]=y;
     21         sz[y]+=sz[x];
     22     }
     23 }
     24 void add(int x,int y,int z){
     25     edge[E].nex=head[x];
     26     edge[E].to=y;
     27     edge[E].len=z;
     28     head[x]=E++;
     29 }
     30 int lca(int x,int y){
     31     if (sh[x]<sh[y])swap(x,y);
     32     for(int i=20;i>=0;i--)
     33         if (sh[f[x][i]]>=sh[y])x=f[x][i];
     34     if (x==y)return x;
     35     for(int i=20;i>=0;i--)
     36         if (f[x][i]!=f[y][i]){
     37             x=f[x][i];
     38             y=f[y][i];
     39         }
     40     return f[x][0];
     41 }
     42 ll dis(int x,int y){
     43     return dep[x]+dep[y]-2*dep[lca(x,y)];
     44 }
     45 void pre_diam(int k,int fa,ll s){
     46     dep[k]=s;
     47     for(int i=head[k];i!=-1;i=edge[i].nex)
     48         if (edge[i].to!=fa)pre_diam(edge[i].to,k,s+edge[i].len);
     49 }
     50 void pre_lca(int k,int fa,ll s,int ss){
     51     sh[k]=ss;
     52     dep[k]=s;
     53     f[k][0]=fa;
     54     for(int i=1;i<=20;i++)f[k][i]=f[f[k][i-1]][i-1];
     55     for(int i=head[k];i!=-1;i=edge[i].nex)
     56         if (edge[i].to!=fa)pre_lca(edge[i].to,k,s+edge[i].len,ss+1);
     57 }
     58 void pre_son(int k,int fa){
     59     f[k][0]=fa;
     60     for(int i=head[k];i!=-1;i=edge[i].nex)
     61         if (edge[i].to!=fa)pre_son(edge[i].to,k);
     62 }
     63 int main(){
     64     scanf("%d",&n);
     65     memset(head,-1,sizeof(head));
     66     for(int i=1;i<n;i++){
     67         scanf("%d%d%d",&x,&y,&z);
     68         add(x,y,z);
     69         add(y,x,z);
     70     }
     71     pre_diam(1,0,0);
     72     x=1;
     73     for(int i=2;i<=n;i++)
     74         if (dep[i]>dep[x])x=i;
     75     pre_diam(x,0,0);
     76     y=1;
     77     for(int i=2;i<=n;i++)
     78         if (dep[i]>dep[y])y=i;
     79     pre_lca(1,1,0,0);
     80     for(int i=1;i<=n;i++)ans[i]=make_pair(max(dis(x,i),dis(y,i)),i);
     81     sort(ans+1,ans+n+1);
     82     pre_son(ans[1].se,0);
     83     scanf("%d",&m);
     84     for(int i=1;i<=m;i++){
     85         scanf("%lld",&l);
     86         int max_ans=0;
     87         for(int j=1;j<=n;j++){
     88             fa[j]=j;
     89             sz[j]=1;
     90         }
     91         for(int j=n,k=n;j;j--){
     92             for(int t=head[ans[j].se];t!=-1;t=edge[t].nex)
     93                 if (edge[t].to!=f[ans[j].se][0])merge(ans[j].se,edge[t].to);
     94             while (ans[k].fi>ans[j].fi+l){
     95                 sz[find(ans[k].se)]--;
     96                 k--;
     97             }
     98             max_ans=max(max_ans,sz[find(ans[j].se)]);
     99         }
    100         printf("%d
    ",max_ans);
    101     }
    102 }
    View Code
  • 相关阅读:
    分梨
    18岁生日
    马的移动
    摆积木
    杭电2093考试排名
    栈的应用——四则运算表达式求值
    用指针实现对二维数组元素的访问
    用多种方法访问字符数组元素
    fread()函数和fwrite()函数进行文件操作
    hdu-1431 素数回文
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/14521924.html
Copyright © 2020-2023  润新知