[noip2016]天天爱跑步 题解

哎,最难第一道题了,昨天才刚刚刚出来,但其实还是有些不太理解,就把自己的想法写一下,说不定哪儿天就融会贯通了呢。

设每一条道路的起点为S,中点为T,它们的lca(最近公共祖先)为LCA,那么,就是求S到lca上dep[x]+w[x]==dep[S](由dep[S]-dep[x]==w[x]变形可得)的数量,lca到T是类似的,不过有一个减号,为了让它变为正,加一个数就好了,具体为dep[x]-w[x]+(N>>1)。

当然直接计算是肯定不行的,可以直接用树链剖分来作,不过太麻烦了,打标记这种东西,树上差分吧。

然后,我就比较迷幻了,不知所措,具体大概就是每次先用进入深搜的点更新up和down,结束时更新一下ans之类的。当然,出去的时候要消除一下影响。

嗯,大概就是这样吧,具体看看代码,我也解释不太清楚,哎。。。

#include<bits/stdc++.h>
#define N 1000000 
using namespace std;
int n,m,x,y,lc,tmp,w[N+5],b;
int f[N+5][25],dep[N+5],ans[N+5];
int first[N+5],nxt[N+5],to[N+5],siz;
int first1[N+5],nxt1[N+5],to1[N+5],siz1;
int first2[N+5],nxt2[N+5],to2[N+5],siz2;
int first3[N+5],nxt3[N+5],to3[N+5],siz3;
int up[N+5],down[N+5],u,d;
int cnt[N+5];
inline char nc()
{
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    int x=0,b=1;
    char c=nc();
    for(;!(c<='9'&&c>='0');c=nc())if(c=='-')b=-1;
    for(;c<='9'&&c>='0';c=nc())x=x*10+c-'0';
    return x*b;
}
inline void write(int x)
{
    if(x==0)putchar('0');
    else
    {
        char buf[15];
        int len=0;
        if(x<0)putchar('-'),x=-x;
        while(x)buf[++len]=x%10+'0',x/=10;
        for(int i=len;i>=1;i--)putchar(buf[i]);
    }
}
inline void add(int x,int y)
{
    nxt[siz]=first[x];
    first[x]=siz;
    to[siz]=y;
    siz++;
}
inline void add1(int x,int y)
{
    nxt1[siz1]=first1[x];
    first1[x]=siz1;
    to1[siz1]=y;
    siz1++;
}
inline void add2(int x,int y)
{
    nxt2[siz2]=first2[x];
    first2[x]=siz2;
    to2[siz2]=y;
    siz2++;
}
inline void add3(int x,int y)
{
    nxt3[siz3]=first3[x];
    first3[x]=siz3;
    to3[siz3]=y;
    siz3++;
}
inline void init(int x,int fa)
{
    dep[x]=dep[fa]+1;
    for(int i=1;i<=20;i++)if(f[x][i-1])f[x][i]=f[f[x][i-1]][i-1];else break;
    for(int i=first[x];i!=-1;i=nxt[i])
    {
        int u=to[i];
        if(u==fa)continue;
        f[u][0]=x;
        init(u,x);
    }
} 
inline void DFS(int x,int f)
{
    int d=down[dep[x]+w[x]],u=up[dep[x]-w[x]+(N>>1)];
    down[dep[x]]+=cnt[x];
    for(int i=first1[x];i!=-1;i=nxt1[i])up[to1[i]+(N>>1)]++;
    for(int i=first[x];i!=-1;i=nxt[i])if(to[i]!=f)DFS(to[i],x);
    ans[x]=down[dep[x]+w[x]]+up[dep[x]-w[x]+(N>>1)]-d-u;
    for(int i=first2[x];i!=-1;i=nxt2[i])
    {
        down[to2[i]]--;
        if(to2[i]==dep[x]+w[x])ans[x]--;
    }
    for(int i=first3[x];i!=-1;i=nxt3[i])up[to3[i]+(N>>1)]--;
}
inline int LCA(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    int fet=dep[x]-dep[y];
    for(int i=20;i>=0;i--)if(fet>=(1<<i))fet-=(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])x=f[x][i],y=f[y][i];
    return f[x][0];
}
int main()
{
    freopen("in.txt","r",stdin);
    memset(first,-1,sizeof(first));
    memset(first1,-1,sizeof(first1));
    memset(first2,-1,sizeof(first2));
    memset(first3,-1,sizeof(first3));
    n=read(),m=read();
    for(int i=1;i<n;i++)x=read(),y=read(),add(x,y),add(y,x);
    init(1,0);
    for(int i=1;i<=n;i++)w[i]=read();
    for(int i=1;i<=m;i++)
    {
        x=read(),y=read(),lc=LCA(x,y);
        tmp=dep[x]+dep[y]-2*dep[lc];b=dep[y]-tmp;cnt[x]++;
        add1(y,b),add2(lc,dep[x]),add3(lc,b);
    }
    DFS(1,0);
    for(int i=1;i<=n;i++)if(i!=n)write(ans[i]),putchar(' ');else write(ans[i]);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值