天天爱跑步

9 篇文章 0 订阅
8 篇文章 0 订阅

天天爱跑步

(File IO): input:running.in output:running.out

Time Limits: 2s Memory Limits: 512MB

Description
Description
Input
Input
Output
Output
Sample Input
Sample Input1:

6 3
2 3
1 2
1 4
4 5
4 6
0 2 5 1 2 3
1 5
1 3
2 6

Sample Input2:

5 3
1 2
2 3
2 4
1 5
0 1 0 3 0
3 1
1 4
5 5

Sample Output
Sample Output1:

2 0 0 1 1 1

Sample Output2:

1 2 1 0 1

Hint
Hint
Data Constraint
Data
Tips


解题思路


以1为根遍历一遍,求出每一个点的的层数fl[i]

对于每一对s,t都会有他们的lca,则s到t的长度len为fl[s]+fl[t]-2fl[lca]我们可以分为s->lca,lca->t考虑:
Solution

对于路径s->lca的点d如图①,在第w[d]个时间点上到达需满足

wd+fld=fls

对于路径lca->t的点d如图②,在第w[d]个时间点上到达需满足
fldfllca+flsfllca=wd
||
lenflt=wdfld

搜完以i为根的子树,我们可以用两个桶分别表示:
1. now[0][x] 为i的子树中,所有能经过i的起点s,fl[s]=x的数量
2. now[1][x] 为i的子树中,所有能经过i的终点t,len-fl[t]=x的数量

则x的节点的答案为now[0][w[x]+fl[x]]-搜完i的子树前的now[0][w[x]-fl[x]]+now[1][w[x]-fl[x]]-搜完i的子树前的now[1][w[x]-fl[x]]

Codes

#include<cstring>
#include<cstdio>
#define lim 100000000
#define ad 600010 
using namespace std;
int to[ad*2],nex[ad*2],fir[ad],las[ad],w[ad],n,m,len;
int data[ad],fl[ad],num[2][ad*2],top,f[20][ad],vfir[2][ad*2],vlas[2][ad*2],vnex[2][ad*2],sum[ad],vtot[2],vv[2][ad*2];
bool bz[300000];

void link(int x,int y)
{
    top++;
    to[top]=y;nex[top]=0;
    if(fir[x])nex[las[x]]=top;else fir[x]=top;
    las[x]=top;
}

void swap(int &a,int &b){int c=a;a=b;b=c;}

void vlink(int x,int s,int v)
{
    int a,b,c,d,e,f;
    int k=++vtot[s];
    vv[s][k]=v;
    vnex[s][k]=0;
    if(vfir[s][x])vnex[s][vlas[s][x]]=k;else vfir[s][x]=k;
    vlas[s][x]=k;
    a=x;b=s;c=v;
    d=vv[s][k];
    e=vfir[s][x];
    f=vlas[s][x];
}

void work(int x)
{
    int s1=num[0][fl[x]+w[x]+ad],s2=num[1][w[x]-fl[x]+ad];
    for(int j=0;j<2;j++)
        for(int i=vfir[j][x];i;i=vnex[j][i])
        {
            int xx=vv[j][i];
            if(xx<=lim)num[j][xx]++;
        }
    for(int i=fir[x];i;i=nex[i])
    {
        if(bz[to[i]])continue;
        bz[to[i]]=1;
        work(to[i]);
    }
    for(int j=0;j<2;j++)
        for(int i=vfir[j][x];i;i=vnex[j][i])
        {
            int xx=vv[j][i];
            if(xx>lim)num[j][xx-lim]--;
        }
    sum[x]+=num[0][ad+fl[x]+w[x]]-s1;
    sum[x]+=num[1][w[x]-fl[x]+ad]-s2;
}

int main()
{
    freopen("running.in","r",stdin);
    freopen("running.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        link(x,y),link(y,x);
    }
    int h=0,t=1;
    data[1]=1;
    fl[1]=0;bz[1]=1;
    while(h<t)
    {
        int now=data[++h];
        for(int i=fir[now];i;i=nex[i])
        {
            int x=to[i];
            if(!bz[x])
            {
                bz[x]=1;
                f[0][x]=now;
                fl[x]=fl[now]+1;
                data[++t]=x;
            }
        }
    }
    for(int i=1;i<=n;i++)scanf("%d",&w[i]);
    for(int j=1;j<20;j++)
        for(int i=1;i<=n;i++)
            if(fl[i]>=(1<<j))
            {
                f[j][i]=f[j-1][f[j-1][i]];
            }
    for(int i=1;i<=m;i++)
    {
        int s,t,lca,tx,ty,temp,co=0;
        scanf("%d%d",&s,&t);
        if(s==t)
        {
            if(w[s]==0)sum[s]++;
            continue;
        }
        tx=s,ty=t;
        if(fl[tx]>fl[ty])swap(tx,ty);
        temp=fl[ty]-fl[tx];
        while(temp)
        {
            if(temp&1)ty=f[co][ty];
            temp>>=1;
            co++;
        }
        if(tx==ty)
        {
            lca=tx;
        }else
        {
            for(int j=19;j+1;j--)
            {
                if((1<<j)>fl[tx])continue;
                if(f[j][ty]!=f[j][tx])
                {
                    tx=f[j][tx];
                    ty=f[j][ty];
                }
            }
            lca=f[0][tx];
        }
        len=fl[s]+fl[t]-fl[lca]*2;
        if(w[lca]==fl[s]-fl[lca])sum[lca]++;
        if(s!=lca)
        {
            vlink(s,0,ad+fl[s]);
            vlink(lca,0,ad+fl[s]+lim);
        }
        if(t!=lca)
        {
            vlink(t,1,ad+len-fl[t]);
            vlink(lca,1,ad+len-fl[t]+lim);
        }
    }
    memset(bz,0,sizeof(bz));
    bz[1]=1;
    work(1);
    for(int i=1;i<=n;i++)printf("%d ",sum[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值