• [BZOJ] 4719: [Noip2016]天天爱跑步


    弱化版题目:关联点

    关联点1

    统计:(v)(x)的子树中,且(dis(x,v)=a_v)(v)u

    也就是要找一个点的k级祖先,可以在dfs时维护一个祖先栈,stack[top-a[v]]即为点x

    关联点2

    统计:(v)(x)的子树中,且(dis(x,v)=a_x)(v)

    也就是找一个点的k级儿子,可以用全局的一个桶bac维护每个点的出现次数,先记录bac[a[x]]的值,对子树dfs后再用新的bac[a[x]]减去原答案即为k级儿子个数

    本题

    把路径拆开,统计每个点对答案的贡献

    设从(u)(v)的一条路径上有一点(x)

    1. x在上升的路径上(u->lca)
    2. x在下降的路径上(lca->v)

    对于情况1,当且仅当(dep[x]+w[x]=dep[u])成立,因此在每个(u)的位置开vector存(dep[u]),对于每个点x统计的是(dep[x]+w[x])

    对于情况2,当且仅当(w[x]-dep[x]=dis(u,v)-dep[v])成立,因此在每个(v)的位置开vector存(dis(u,v)-dep[v]),对于每个点x统计的是(w[x]-dep[x])

    实际上这样会算重,lca的位置会重复贡献,因此还要开两个vector记录经过lca路径的起点和终点,也就是情况1、2统计的内容

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cctype>
    #include<vector>
    
    using namespace std;
    inline int rd(){
      int ret=0,f=1;char c;
      while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
      while(isdigit(c))ret=ret*10+c-'0',c=getchar();
      return ret*f;
    }
    
    const int MAXN = 300005;
    
    int n,m,W[MAXN];
    vector<int> v1[MAXN],v2[MAXN],v3[MAXN],v4[MAXN];
    struct Edge{
      int nex,to;
    }e[MAXN<<1];
    int ecnt,head[MAXN];
    inline void add(int x,int y){
      e[++ecnt].nex = head[x];
      e[ecnt].to = y;
      head[x] = ecnt;
    }
    
    int fa[MAXN],hs[MAXN],dep[MAXN];
    int sp1(int x,int pre){
    //  cout<<x<<endl;
      fa[x]=pre;dep[x]=dep[pre]+1;
      int mx=0,siz=1,tmp;
      for(int i=head[x];i;i=e[i].nex){
        int v=e[i].to;
        if(v==pre)continue;
        tmp=sp1(v,x);siz+=tmp;
        if(tmp>mx){mx=tmp;hs[x]=v;}
      }
      return siz;
    }
    int top[MAXN];
    void sp2(int x,int tp){
      top[x]=tp;
      if(hs[x])sp2(hs[x],tp);
      for(int i=head[x];i;i=e[i].nex){
        int v=e[i].to;
        if(v==fa[x]||v==hs[x])continue;
        sp2(v,v);
      }
    }
    int lca(int x,int y){
      while(top[x]!=top[y])
        dep[top[x]]<dep[top[y]]?y=fa[top[y]]:x=fa[top[x]];
      return dep[x]<dep[y]?x:y;
    }
    int bac[MAXN<<1],ans[MAXN];
    const int offset=300005;
    void dfs1(int x){
      int pre=bac[dep[x]+W[x]];
      int sz=v1[x].size();
      for(int i=0;i<sz;i++)bac[v1[x][i]]++;
      for(int i=head[x];i;i=e[i].nex){
        int v=e[i].to;if(v==fa[x])continue;
        dfs1(v);
      }
      ans[x]+=bac[dep[x]+W[x]]-pre;
      sz=v4[x].size();
      for(int i=0;i<sz;i++)bac[v4[x][i]]--;
    }
    void dfs2(int x){
      int pre=bac[W[x]-dep[x]+offset];
      int sz=v2[x].size();
      for(int i=0;i<sz;i++)bac[v2[x][i]+offset]++;
      sz=v3[x].size();
      for(int i=0;i<sz;i++)bac[v3[x][i]+offset]--;
      for(int i=head[x];i;i=e[i].nex){
        int v=e[i].to;if(v==fa[x])continue;
        dfs2(v);
      }
      ans[x]+=bac[W[x]-dep[x]+offset]-pre;
    }
    int main(){
      n=rd();m=rd();
      int x,y;
      for(int i=1;i<=n-1;i++){
        x=rd();y=rd();
        add(x,y);add(y,x);
      }
      sp1(1,0);sp2(1,1);
    
      for(int i=1;i<=n;i++)W[i]=rd();
      for(int i=1;i<=m;i++){
        x=rd();y=rd();int l=lca(x,y);
        v1[x].push_back(dep[x]);
        v2[y].push_back((dep[x]-(dep[l]<<1)));
        v3[l].push_back((dep[x]-(dep[l]<<1)));
        v4[l].push_back(dep[x]); 
      }
      dfs1(1);
      memset(bac,0,sizeof(bac));
      dfs2(1);
      for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
      return 0;
    }
    
    
    未经许可,禁止搬运。
  • 相关阅读:
    gets_s()函数的参数太少,strcpy_s():形参和实参 2 的类型不同,等c函数在Visual Studio上出现的问题, get()函数和scanf()读取字符串的区别,栈的随机性
    线性表的顺序存储实现
    汉诺塔问题, 用递归方法求集合中的中位数
    共用体union
    洛谷3384:【模板】树链剖分——题解
    BZOJ4196:[NOI2015]软件包管理器——题解
    BZOJ3140:[HNOI2013]消毒——题解
    BZOJ1059:[ZJOI2007]矩阵游戏——题解
    洛谷4277:萃香的请柬——题解
    BZOJ1854:[SCOI2010]连续攻击游戏——题解
  • 原文地址:https://www.cnblogs.com/ghostcai/p/9776137.html
Copyright © 2020-2023  润新知