天天爱跑步

传送门

终于拿下NOIP最难一题

看别人的题解看不懂

于是准备写一篇更通俗易懂杂乱无章的题解

解法:

树上差分+线段树(桶)+LCA

将每条路径在LCA处切成上升路径和下降路径

会发现对于x号观察员若观察到玩家i

则必有
dep[i]=w[x]+depx

dep[i]-2*dep[lca]=w[x]-depx

我们用桶标记(桶用动态开点线段树实现)

具体实现:
用两种线段树统计 第一种统计上升段 第二种统计下降段

若一个玩家从s到t点 记L=LCA(s,t)
则在s节点加上上升段该值标记(dep[s])
在t节点加上下降段该值标记(dep[s]-2dep[L])
则为了避免在L处统计两次
下降段在L处删除上述标记(dep[s]-2
dep[L])
上升段在L的父亲处删除上述标记(dep[s])

然后一遍dfs
对于每个点x
将其子节点的线段树都合并起来
并加上/删去该点的标记值
对于上升段的线段树
求w[i]-dep[x]的次数
对于下降段的线段树
求w[i]+dep[x]的次数

最后x点答案即为上面两次求的答案之和

总复杂度 O(\(n\log n\))

代码:

200行代码 还好没出祸。。。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<map>
#define inf 2000000000
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dwn(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef long long ll;
int n,m,root=1,a[300010];
int dep[300010],f[300010][20],lg2[300010];
int tot=1,head[300010];
queue<int> que;
int tot1=0,rt1[300010],up[6000010],ul[6000010],ur[6000010];
int tot2=0,rt2[300010],dn[6000010],dl[6000010],dr[6000010];
vector<int> add1[300010],add2[300010],del1[300010],del2[300010];
int ans[300010];
struct EDGE
{
    int nxt,to;
}edge[600010];
void adde(int u,int v)
{
    edge[++tot].nxt=head[u];
    edge[tot].to=v;
    head[u]=tot;
}
int read()
{
    int x=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9')
    {
        x=(x<<3)+(x<<1)+(ch^48);
        ch=getchar();
    }
    return x;
}
void bfs()
{
    dep[root]=1;
    que.push(root);
    while(!que.empty())
    {
        int x=que.front();que.pop();
        for(int i=head[x];i;i=edge[i].nxt)
        {
            int y=edge[i].to;
            if(dep[y]) continue;
            dep[y]=dep[x]+1;
            f[y][0]=x;
            rep(i,1,lg2[dep[y]])
            {
                f[y][i]=f[f[y][i-1]][i-1];
            }
            que.push(y);
        }
    }
}
int lca(int x,int y)
{
    if(dep[x]>dep[y]) swap(x,y);
    dwn(i,lg2[dep[y]-dep[x]],0)
        if(dep[f[y][i]]>=dep[x]) y=f[y][i];
    if(x==y) return x;
    dwn(i,lg2[dep[x]],0)
        if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
void adup(int &k,int l,int r,int x,int d)
{
    if(!k) k=++tot1;
    if(l==r)
    {
        up[k]+=d;
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid) adup(ul[k],l,mid,x,d);
    else adup(ur[k],mid+1,r,x,d);
    up[k]=up[ul[k]]+up[ur[k]];
}
void addn(int &k,int l,int r,int x,int d)
{
    if(!k) k=++tot2;
    if(l==r)
    {
        dn[k]+=d;
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid) addn(dl[k],l,mid,x,d);
    else addn(dr[k],mid+1,r,x,d);
    dn[k]=dn[dl[k]]+dn[dr[k]];
}
int askup(int &k,int l,int r,int x)
{
    if(!k) return 0;
    if(l==r)
    {
        return up[k];
    }
    int mid=(l+r)>>1;
    if(x<=mid) return askup(ul[k],l,mid,x);
    else return askup(ur[k],mid+1,r,x);
}
int askdn(int &k,int l,int r,int x)
{
    if(!k) return 0;
    if(l==r)
    {
        return dn[k];
    }
    int mid=(l+r)>>1;
    if(x<=mid) return askdn(dl[k],l,mid,x);
    else return askdn(dr[k],mid+1,r,x);
}
int mergeup(int p,int q,int l,int r)
{
    if(!p) return q;
    if(!q) return p;
    if(l==r)
    {
        up[p]+=up[q];
        return p;
    }
    int mid=(l+r)>>1;
    ul[p]=mergeup(ul[p],ul[q],l,mid);
    ur[p]=mergeup(ur[p],ur[q],mid+1,r);
    up[p]=up[ul[p]]+up[ur[p]];
    return p;
}
int mergedn(int p,int q,int l,int r)
{
    if(!p) return q;
    if(!q) return p;
    if(l==r)
    {
        dn[p]+=dn[q];
        return p;
    }
    int mid=(l+r)>>1;
    dl[p]=mergedn(dl[p],dl[q],l,mid);
    dr[p]=mergedn(dr[p],dr[q],mid+1,r);
    dn[p]=dn[dl[p]]+dn[dr[p]];
    return p;
}
void dfs(int x)
{
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int y=edge[i].to;
        if(y==f[x][0]) continue;
        dfs(y);
        rt1[x]=mergeup(rt1[x],rt1[y],1,2*n);
        rt2[x]=mergedn(rt2[x],rt2[y],1,2*n);
    }
    for(int i=0;i<add1[x].size();++i)
        adup(rt1[x],1,2*n,add1[x][i],1);
    for(int i=0;i<add2[x].size();++i)
        addn(rt2[x],1,2*n,add2[x][i],1);
    for(int i=0;i<del1[x].size();++i)
        adup(rt1[x],1,2*n,del1[x][i],-1);
    for(int i=0;i<del2[x].size();++i)
        addn(rt2[x],1,2*n,del2[x][i],-1);
    ans[x]+=askup(rt1[x],1,2*n,a[x]+dep[x]);
    ans[x]+=askdn(rt2[x],1,2*n,a[x]-dep[x]+n);
}
int main()
{
    n=read(),m=read();
    rep(i,2,n) lg2[i]=lg2[i>>1]+1;
    rep(i,1,n-1)
    {
        int u=read(),v=read();
        adde(u,v),adde(v,u);
    }
    rep(i,1,n) a[i]=read();
    bfs();
    rep(i,1,m)
    {
        int s=read(),t=read();
        int l=lca(s,t);
        add1[s].push_back(dep[s]);
        add2[t].push_back(dep[s]-2*dep[l]+n);
        del1[f[l][0]].push_back(dep[s]);
        del2[l].push_back(dep[s]-2*dep[l]+n);
    }
    dfs(root);
    rep(i,1,n)
    {
        printf("%d ",ans[i]);
    }
    return 0;
}

posted @ 2019-07-11 13:55  zmy蒟蒻  阅读(346)  评论(0)    收藏  举报