1. 程式人生 > >bzoj4719: [Noip2016]天天愛跑步 樹上差分

bzoj4719: [Noip2016]天天愛跑步 樹上差分

-a 用兩個 output 玩家 name cst while con bzoj

Description

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

Input

第一行有兩個整數N和M 。其中N代表樹的結點數量, 同時也是觀察員的數量, M代表玩家的數量。
接下來n-1 行每行兩個整數U和V ,表示結點U 到結點V 有一條邊。
接下來一行N 個整數,其中第個整數為Wj , 表示結點出現觀察員的時間。
接下來 M行,每行兩個整數Si和Ti,表示一個玩家的起點和終點。
對於所有的數據,保證 。
1<=Si,Ti<=N,0<=Wj<=N

Output

輸出1行N 個整數,第個整數表示結點的觀察員可以觀察到多少人。

技術分享圖片

首先可以得到兩個式子

\(dep[u]-dep[x]=w[x]\)

\(dep[u]-2*dep[lca]+dep[x]=w[x]\)

換位得到

\(w[x]+dep[x]=dep[u]\)

\(w[x]-dep[x]=dep[u]-2*dep[lca]\)

\(x\)的值確定的時候,左邊的值是確定的,因此我們考慮用x的子樹更新\(x\)的答案。
用兩個桶記錄值,每\(dfs\)到一個點更新桶的值的個數。
\(dep[u]\)\(dep[u]-2*dep[lca]\)更新桶的情況,
\(w[x]+dep[x]\)\(w[x]-dep[x]\)查詢\(dfs\)\(x\)點時的答案。

註意\(w[x]-dep[x]\)的答案可能為負,所以要加上\(maxn\)

回溯時更新當前節點的答案,答案更新時減去父親節點的答案

對於\(lca\)

等於起點和終點的特殊考慮。
另外,對於分裂成兩條鏈\(lca\)可能會被統計兩遍,最後特殊判斷一下,如果被統計了兩遍就減去一遍,

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector> 
const int maxn=3e5+10;
const int k=448;
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Dec(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch==‘-‘)f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
int n,m;
int fa[maxn],w[maxn],s[maxn],t[maxn];
vector<int>mp[maxn];
int son[maxn],size[maxn],top[maxn],dep[maxn];
struct que{int v,tag;};
vector<que>w1[maxn];
vector<que>w2[maxn];
int ans[maxn],lca[maxn];
void dfs1(int x,int f,int d){
    fa[x]=f;
    dep[x]=d;
    size[x]=1;
    for(int i=0;i<(int)mp[x].size();i++){
        int u=mp[x][i];
        if(u==f)continue;
        dfs1(u,x,d+1);
        size[x]+=size[u];
        if(!son[x]||size[son[x]]<size[u])son[x]=u;
    }
}
void dfs2(int x,int t){
    top[x]=t;
    if(!son[x])return ;
    dfs2(son[x],t);
    for(int i=0;i<(int)mp[x].size();i++){
        int u=mp[x][i];
        if(u==son[x]||u==fa[x])continue;
        dfs2(u,u);
    }
}
int LCA(int x,int y){
    int fx=top[x],fy=top[y];
    while(fx!=fy){
        if(dep[fx]>dep[fy])x=fa[fx];
        else y=fa[fy];
        fx=top[x];fy=top[y];
    }
    if(dep[x]>dep[y])return y;
    else return x;
}
int bac1[2*maxn],bac2[2*maxn];
void dfs(int x,int a,int b){
    for(int i=0;i<(int)w1[x].size();i++)
//    For(i,0,(int)w1[x].size()-1) //不強制轉換會RE 
        bac1[w1[x][i].v+maxn]+=w1[x][i].tag;//更新桶內情況
    for(int i=0;i<(int)w2[x].size();i++)
//    For(i,0,(int)w2[x].size()-1) 
        bac2[w2[x][i].v+maxn]+=w2[x][i].tag;//更新桶內情況
   for(int i=0;i<(int)mp[x].size();i++){
        int u=mp[x][i];
        if(u==fa[x])continue;
        dfs(u,bac1[dep[u]+w[u]+maxn],bac2[w[u]-dep[u]+maxn]);
    }
    ans[x]+=bac1[dep[x]+w[x]+maxn]+bac2[w[x]-dep[x]+maxn]-a-b;//a,b是父親節點的答案,要減去父親節點的情況就是當前節點的結果
}
int main()
{
    n=read();m=read();
    For(i,1,n-1){
        int u=read(),v=read();
        mp[u].push_back(v);
        mp[v].push_back(u);
    }
    For(i,1,n)w[i]=read();
    For(i,1,m)s[i]=read(),t[i]=read();
    dfs1(1,0,0);
    dfs2(1,1);
    For(i,1,m)lca[i]=LCA(s[i],t[i]);
    For(i,1,m){
        if(lca[i]==t[i]){
            w1[s[i]].push_back((que){dep[s[i]],1});
            w1[fa[t[i]]].push_back((que){dep[s[i]],-1});
        }
        else if(lca[i]==s[i]){
            w2[t[i]].push_back((que){dep[s[i]]-2*dep[lca[i]],1});
            w2[fa[s[i]]].push_back((que){dep[s[i]]-2*dep[lca[i]],-1});
        }
        else{
            if(w[lca[i]]+dep[lca[i]]==dep[s[i]])--ans[lca[i]];
            w1[s[i]].push_back((que){dep[s[i]],1});
            w1[fa[lca[i]]].push_back((que){dep[s[i]],-1});
            w2[t[i]].push_back((que){dep[s[i]]-2*dep[lca[i]],1});
            w2[fa[lca[i]]].push_back((que){dep[s[i]]-2*dep[lca[i]],-1});
        }
    }
    dfs(1,0,0);
    For(i,1,n)printf("%d ",max(0,ans[i]));
    return 0;
}

bzoj4719: [Noip2016]天天愛跑步 樹上差分