天天爱跑步NOIP

首先,我们考虑对于一条路径



从x->y,可以把它拆分成两部分,图中用虚线分开,然后这条路径就变成了x->lca,son[lca]->y

先来考虑从x向上走到lca的路径,对于这条路上的节点i,玩家能对节点i产生贡献的前提是deep[x]-w[i]=deep[i]

移项可得deep[x]=w[i]+deep[i],也就是说起点在w[i]+deep[i]这个深度的玩家向上走的时候都会对节点i产生1的贡献,(玩家想对节点i产生贡献,那它的深度一定等于w[i]+deep[i])

对于每一层深度建一颗线段树,动态开点,先dfs整个树,求出每个节点的dfs序,那么这就转化成区间问题,对于每一个玩家s->t,在深度为deep[s]的线段树id[s]的位置加1,id[fa[lca(s,t)]]-1,利用了差分思想,每一次求一下节点i在深度为w[i]+deep[i]的线段树里[in[i],out[i]]区间和就是答案,利用差分可以防止不合法的计算,id[fa[lca(s,t)]]-1,保证x不会被lca以上的节点计算贡献

然后考虑向下的路径,对于son[lca]->y的路径上的节点j,玩家能对j产生贡献前提是deep[x]+deep[y]-2*deep[lca(x,y)]-w[j]=deep[y]-deep[j]

移项可得deep[x]-2*deep[lca(x,y)]=w[j]-deep[j]相应的,我们在deep[x]-2*deep[lca]深度的线段树id[x]的位置加1,lca处减1,然后对于每个节点,查询w[j]-deep[j]深度的线段树[in[j],out[j]]即可

注意红字部分可能出现负数,下标统一加n

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define maxn 300005
using namespace std;
int n,m;
int fa[maxn],d[maxn*5],ans[maxn];
struct edge
{
    int to,ne;  
}b[maxn*2];
int kk=0,head[maxn];
struct edge2
{
    int fr,to,len,anc;  
}s[maxn*2];
int w[maxn];
int f[maxn][35];
inline int read()
{
     int x=0;
     char ch=getchar();
     while(ch<'0'||ch>'9') ch=getchar();
     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
     return x;  
}
inline void add(int u,int v)
{
     kk++;
     b[kk].to=v;b[kk].ne=head[u];head[u]=kk; 
}
inline void init()
{
     memset(head,-1,sizeof(head));
     memset(f,-1,sizeof(f));
}
void pre()
{ 
     for(int i=1;i<=n;i++) f[i][0]=fa[i];
     for(int j=1;(1<<j)<=n;j++)
       for(int i=1;i<=n;i++)
       if(f[i][j-1]!=-1) f[i][j]=f[f[i][j-1]][j-1];
}
int lca(int x,int y)
{
    //printf("%d %d\n",x,y);
     if(d[x]<d[y]) swap(x,y);
     int dc=d[x]-d[y];
     for(int i=0;dc;i++)
     if((dc&(1<<i))){
        dc^=(1<<i);
        x=f[x][i];
     }
     if(x==y) return x;
     for(int i=20;i>=0;i--)
     if(f[x][i]!=f[y][i]&&f[x][i]!=-1){
        x=f[x][i];   y=f[y][i];
     }
     return fa[x];
}
int q[maxn],h[maxn],id=0;
void dfs(int x)
{
     q[x]=++id;
     for(int i=head[x];i!=-1;i=b[i].ne)
     if(b[i].to!=fa[x]){
         fa[b[i].to]=x; d[b[i].to]=d[x]+1;
         dfs(b[i].to);
     }
     h[x]=id;
}
int root[maxn*30],sum[maxn*30],ls[maxn*30],rs[maxn*30];
int cnt=0;
struct Tree
{
    void update(int k)
    {
        sum[k]=sum[ls[k]]+sum[rs[k]]; 
    }
    void insert(int &k,int l,int r,int x,int w)
    {
        if(!x) return ;
        if(!k) k=++cnt;
        sum[k]+=w;
        if(l==r) return ;
        int mid=(l+r)/2;
        if(x<=mid) insert(ls[k],l,mid,x,w);
        else insert(rs[k],mid+1,r,x,w);
        update(k);
    }  
    int quiry(int k,int l,int r,int ll,int rr)
    {
        if(!k) return 0;
        if(ll<=l&&r<=rr) return sum[k];
        int ans=0;
        int mid=(l+r)/2;
        if(ll<=mid) ans+=quiry(ls[k],l,mid,ll,rr);
        if(rr>mid)  ans+=quiry(rs[k],mid+1,r,ll,rr);
        return ans;
    } 
}tr;
inline void init2()
{
    memset(root,0,sizeof(root));
    memset(sum,0,sizeof(sum));
    memset(ls,0,sizeof(ls));
    memset(rs,0,sizeof(rs));
    cnt=0;
}
int main()
{
    freopen("runninga.in","r",stdin);
    freopen("runninga.out","w",stdout);
    init();
    n=read();m=read();
    int x,y,anc;
    for(int i=1;i<n;i++){
        x=read(); y=read();
        add(x,y); add(y,x);
    }
    dfs(1);
    pre();
    for(int i=1;i<=n;i++) w[i]=read();
    for(int i=1;i<=m;i++){
       s[i].fr=read(); s[i].to=read();
       anc=lca(s[i].fr,s[i].to);
       s[i].len=d[s[i].fr]+d[s[i].to]-2*d[anc];
       s[i].anc=anc;
       tr.insert(root[d[s[i].fr]],1,n,q[s[i].fr],1);
       tr.insert(root[d[s[i].fr]],1,n,q[fa[anc]],-1);  
    }
    for(int i=1;i<=n;i++) ans[i]+=tr.quiry(root[w[i]+d[i]],1,n,q[i],h[i]);
    init2();
    for(int i=1;i<=m;i++){
       int t=d[s[i].fr]-(d[s[i].anc]<<1)+(n<<1);
       tr.insert(root[t],1,n,q[s[i].to],1);
       tr.insert(root[t],1,n,q[s[i].anc],-1);
    }
    for(int i=1;i<=n;i++)
      ans[i]+=tr.quiry(root[w[i]-d[i]+(n<<1)],1,n,q[i],h[i]);     
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    return 0;
}


posted @ 2017-08-14 21:32  HunterxHunterl  阅读(143)  评论(0编辑  收藏  举报