1. 程式人生 > >【NOIP2016提高組復賽day2】天天愛跑步

【NOIP2016提高組復賽day2】天天愛跑步

分享 csdn .net ons src 情況 自己的 for net

題目

小 C 同學認為跑步非常有趣,於是決定制作一款叫做《天天愛跑步》的遊戲。 《天天愛跑步》是一個養成類遊戲,需要玩家每天按時上線,完成打卡任務。 這個遊戲的地圖可以看作一棵包含 n 個結點和 n ? 1 條邊的樹,每條邊連接兩個結點,且任意兩個結點存在一條路徑互相可達。樹上結點編號為從 1 到 n 的連續正整數。 現在有 m 個玩家,第 i 個玩家的起點為 Si ,終點為 Ti 。每天打卡任務開始時,所 有玩家在第 0 秒同時從自己的起點出發,以每秒跑一條邊的速度,不間斷地沿著 最短 路徑向著自己的終點跑去,跑到終點後該玩家就算完成了打卡任務。(由於地圖是一棵樹,所以每個人的路徑是唯一的)
小 C 想知道遊戲的活躍度,所以在每個結點上都放置了一個觀察員。在結點 j 的 觀 察員會選擇在第 Wj 秒觀察玩家,一個玩家能被這個觀察員觀察到當且僅當該玩家 在第 Wj 秒也正好到達了結點 j 。小 C 想知道每個觀察員會觀察到多少人?

註意:我們認為一個玩家到達自己的終點後該玩家就會結束遊戲,他不能等待一 段時間後再被觀察員觀察到。即對於把結點 j 作為終點的玩家:若他在第 Wj 秒前到達 終點,則在結點 j 的觀察員不能觀察到該玩家;若他正好在第 Wj 秒到達終點,則在結 點 j 的觀察員可以觀察到這個玩家。

分析

假設有一條路徑(x->y),最近公共祖先lca,
考慮i這個點是否能觀察到這個玩家,
才成兩條路徑,分兩種情況,

一、(x->lca)

技術分享圖片
如果要使i可以觀察到,
i一定在(x->lca)上
那麽deep[i]+w[i]=deep[x]。
建一個桶,
在x入棧時將deep[x]加入到桶中,
在lca退棧時,將deep[x]踢掉。

二、(lca的某個兒子且為j的祖先->y)

技術分享圖片
如果要使i可以觀察到,
i一定在(lca的某個兒子且為j的祖先->y)上
那麽deep[i]-w[i]=deep[y]-t(t=deep[x]+deep[y]-deep[lca]*2)。
再建一個桶,
同樣
在y入棧時將deep[y]-t加入到同中,
lca的某個兒子且為j的祖先退棧時,將deep[y]-t踢掉。

因為會算重,所以將做完以i為根的子樹後的值-i進棧時的值就是i的答案。

#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
using namespace std;
const int N=700005;
int deep[N],next[N],last[N],to[N],deep1[N],n,m,w[N],tot,ans[N],a[N][2],g[N][20],sum,mxd,t[N*4],t1[N*4];
int next1[N],last1[N],to1[N],tot1;
int next2[N],last2[N],to2[N],tot2;
int next3[N],last3[N],to3[N],tot3;
int next4[N],last4[N],to4[N],tot4;
int bj(int x,int y){next[++tot]=last[x];last[x]=tot;to[tot]=y;}
int bj1(int x,int y){next1[++tot1]=last1[x];last1[x]=tot1;to1[tot1]=y;}
int bj2(int x,int y){next2[++tot2]=last2[x];last2[x]=tot2;to2[tot2]=y;}
int bj3(int x,int y){next3[++tot3]=last3[x];last3[x]=tot3;to3[tot3]=y;}
int bj4(int x,int y){next4[++tot4]=last4[x];last4[x]=tot4;to4[tot4]=y;}
int dg(int x)
{
    for(int i=last[x];i;i=next[i])
    {
        int j=to[i];
        if(j!=g[x][0])
        {
            g[j][0]=x;
            deep[j]=deep[x]+1;
            dg(j);
        }
    }
}
int lca(int x,int y)
{
    if(deep[x]>deep[y])
    {
        int o=x;
        x=y;
        y=o;
    }
    for(int j=log2(n);j>=0;j--)
    {
        if(deep[g[y][j]]>=deep[x])
            y=g[y][j];
    }
    for(int j=log2(n);j>=0;j--)
    {
        if(g[y][j]!=g[x][j])
            y=g[y][j],x=g[x][j];
    }
    if(x!=y) y=g[y][0],x=g[x][0];
    return x;
}
int dg1(int x)
{
    int xx=t[deep[x]+w[x]]+t1[deep[x]-w[x]+N];
    for(int i=last1[x];i;i=next1[i]) t[to1[i]]++;
    for(int i=last3[x];i;i=next3[i]) t1[to3[i]+N]++;
    for(int i=last[x];i;i=next[i])
        if(to[i]!=g[x][0]) dg1(to[i]);
    ans[x]=t[deep[x]+w[x]]+t1[deep[x]-w[x]+N]-xx;
    for(int i=last2[x];i;i=next2[i]) t[to2[i]]--;
    for(int i=last4[x];i;i=next4[i]) t1[to4[i]+N]--;
}
int main()
{
    freopen("running.in","r",stdin);
    freopen("running.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n-1;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        bj(x,y);
        bj(y,x);
    }
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    deep[1]=1;
    dg(1);
    for(int j=1;j<=log2(n);j++)
        for(int i=1;i<=n;i++)
            g[i][j]=g[g[i][j-1]][j-1];
    for(int i=1;i<=m;i++) 
    {
        int x,y;
        scanf("%d%d",&x,&y);
        int top=lca(x,y),t=deep[x]+deep[y]-2*deep[top];
        if(top==x)
        {
            bj3(y,deep[y]-t);
            bj4(x,deep[y]-t);
        }
        else
        if(top==y)
        {
            bj1(x,deep[x]);
            bj2(y,deep[x]);
        }
        else
        {
            bj1(x,deep[x]);
            bj2(top,deep[x]);
            bj3(y,deep[y]-t);
            for(int j=last[top];j;j=next[j])
            {
                if(to[j]!=g[top][0] && lca(to[j],y)==to[j])
                {
                    bj4(to[j],deep[y]-t);
                    break;
                } 
            }
        }
    }
    dg1(1);
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    return 0;
}

【NOIP2016提高組復賽day2】天天愛跑步