[uoj261][NOIP2016]天天爱跑步——树上差分+桶 大佬们的博客 Some Links

思路:

题目所要求的是每一个观测点能够看到的人的个数,所以这个观测点首先必须要在这条路径内。其次就是这个观测点和路径起点的距离要等于 w i ,为了便于操作把这棵树转化成1为根的有根树,然后每一条路径就是一条折线。我们把每个点满足它可以观测到的路径的条件转化为只和路径相关,设路径起点为 u ,终点为 v ,观测点为 i ,一条路通过lca分开成了两条路之后,对于上升的部分要满足这个式子: w i + d e p i = d e p u ,下降的部分则要满足这个式子: d e p i w i = d e p v L e n
那么题目就可以看成对于每一个观测点,分两次来处理每条路径上升的部分和下降的部分,对于每一次处理,求覆盖它的并且等式左边的值都是同一个定值的个数。我们通过树上差分来得到每一个点所有的覆盖它的路径,并且用一个桶来存下这些路径等式右边的值,桶的维护复杂度是线性的。
注意到我们只可以开一个桶,而树上差分是求整个子树内的状态叠加,所以这里改成按照dfs顺序依次差分,查询答案只需要计算子树访问前后的差值就好了。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;

using namespace std;

void File(){
    freopen("uoj261.in","r",stdin);
    freopen("uoj261.out","w",stdout);
}

template<typename T>void read(T &_){
    T __=0,mul=1; char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')mul=-1;
        ch=getchar();
    }
    while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    _=__*mul;
}

const int maxn=3e5+10;
const int maxm=3e5+10;
int n,m,S[maxm],T[maxm],w[maxn],st[maxn][30],dep[maxn];
int beg[maxn],las[maxn<<1],to[maxn<<1],cnte=1;
int Len[maxn],tona[maxn<<1],tonb[maxn<<1],ans[maxn];
vector<int>taga[maxn],tagb[maxn];

void add(int u,int v){
    las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v;
    las[++cnte]=beg[v]; beg[v]=cnte; to[cnte]=u;
}

void dfs(int u,int f){
    st[u][0]=f; dep[u]=dep[f]+1;
    for(int i=beg[u];i;i=las[i]){
        if(to[i]==f)continue;
        dfs(to[i],u);
    }
}

int Log(int x){return floor(log(x)/log(2));}

int find(int u,int v){
    if(dep[v]>dep[u])swap(u,v);
    while(dep[u]!=dep[v])u=st[u][Log(dep[u]-dep[v])];
    if(u==v)return u;
    for(int k=25;k>=0 && u!=v;--k)
        if(st[u][k] && st[v][k] && st[u][k]!=st[v][k])
            u=st[u][k],v=st[v][k];
    return st[u][0];
}

void solve(int u){
    ans[u]-=tona[w[u]+dep[u]]+tonb[dep[u]-w[u]+maxn];
    int siza=taga[u].size()-1,sizb=tagb[u].size()-1;
    REP(i,0,siza)if(taga[u][i]>0)++tona[taga[u][i]];
    else --tona[-taga[u][i]];
    REP(i,0,sizb)if(tagb[u][i]>0)++tonb[tagb[u][i]];
    else --tonb[-tagb[u][i]];
    for(int i=beg[u];i;i=las[i]){
        if(to[i]==st[u][0])continue;
        solve(to[i]);
    }
    ans[u]+=tona[w[u]+dep[u]]+tonb[dep[u]-w[u]+maxn];
}

void init(){
    read(n); read(m);
    int u,v;
    REP(i,1,n-1)read(u),read(v),add(u,v);
    REP(i,1,n)read(w[i]);
    dfs(1,0);
    REP(i,1,26)REP(j,1,n){
        if((1<<i)>=dep[j])continue;
        st[j][i]=st[st[j][i-1]][i-1];
    }
    REP(i,1,m){
        read(u); read(v);
        int lca=find(u,v);
        Len[i]=dep[u]+dep[v]-(dep[lca]<<1);
        taga[u].push_back(dep[u]);             taga[st[lca][0]].push_back(-dep[u]);
        tagb[v].push_back(dep[v]-Len[i]+maxn); tagb[lca].push_back(-dep[v]+Len[i]-maxn);
    }
}

int main(){
    File();
    init();
    solve(1);
    REP(i,1,n)printf("%d\n",ans[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/81392696