1. 程式人生 > >NOIP2016 day1T2--BZOJ4719 天天愛跑步--LCA+差分

NOIP2016 day1T2--BZOJ4719 天天愛跑步--LCA+差分

Description

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

將每條從u到v的路徑分成兩部分,一部分從u到lca,一部分從lca到v。

先考慮從u到lca的部分:對於所有在這條鏈上的節點,如果

w[i]=dep[u]dep[i] dep[u]=dep[i]w[i]則能夠看到玩家。
對於從lca到v的部分:如果w[i]=dep[u]+dep[i]2dep[lca]2dep[lca]dep[u]=dep[i]w[i]則能夠看到玩家。
接下來,問題就轉化成求某一個節點所在的鏈中上的dep[u]2dep[lca]dep[u] 某一特定值的個數。
以dep[u]為例,只要維護g[i]表示當前dep[u]=i的節點個數,要將某一條鏈上的某一個值加1,只要將這條鏈的尾端的值加1,並將這條鏈頂端的父節點的值減1即可,由於lca上的節點與如果利用差分來考慮,可能會記重複,所以我們把lca單獨考慮,這樣就應該在這條鏈的頂端的節點的值減1。最後統計的時候就是自底向上統計,又因為g是全域性陣列,所以記的不僅僅是當前節點的子樹的資訊,所以可以先記下統計子樹前的值,統計完以後再用後面的值減去這個值就是答案。

時間複雜度:O(nlogn)

最後附上程式碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 300006
#define zero maxn*2
using namespace std;
int _read(){
    char ch=getchar();int sum=0;
    while(!(ch>='0'&&ch<='9'))ch=getchar();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=getchar();
    return
sum; } int n,m,tot,_tot[2],w[maxn],ans[maxn],_lnk[2][maxn],lnk[maxn],_son[2][maxn*2],son[maxn*2],_nxt[2][maxn*2],nxt[maxn*2], p[2][maxn*2],dep[maxn],hsh1[4*maxn],hsh2[4*maxn],f[maxn][20]; bool vis[maxn]; void add(int x,int y){ nxt[++tot]=lnk[x];son[tot]=y;lnk[x]=tot; } void _add(int tt,int x,int y,int z){ _nxt[tt][++_tot[tt]]=_lnk[tt][x];_son[tt][_tot[tt]]=y;p[tt][_tot[tt]]+=z;_lnk[tt][x]=_tot[tt]; } void dfs(int x){ vis[x]=0; for(int j=lnk[x];j;j=nxt[j]) if(vis[son[j]]){ dep[son[j]]=dep[x]+1;f[son[j]][0]=x; dfs(son[j]); } } void work(){ memset(vis,1,sizeof(vis));f[1][0]=1; dfs(1); for(int j=1;j<=19;j++) for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1]; } int LCA(int x,int y){ if(dep[x]<dep[y])swap(x,y); for(int j=19;j>=0;j--) if(dep[f[x][j]]>=dep[y])x=f[x][j]; if(x==y)return x; for(int j=19;j>=0;j--) if(f[x][j]!=f[y][j])x=f[x][j],y=f[y][j]; return f[x][0]; } void dfs1(int x){ vis[x]=0;int p1=hsh1[zero+dep[x]+w[x]],p2=hsh2[zero+dep[x]-w[x]]; for(int j=_lnk[0][x];j;j=_nxt[0][j])hsh1[_son[0][j]+zero]+=p[0][j]; for(int j=_lnk[1][x];j;j=_nxt[1][j])hsh2[_son[1][j]+zero]+=p[1][j]; for(int j=lnk[x];j;j=nxt[j]) if(vis[son[j]])dfs1(son[j]); ans[x]+=hsh1[zero+dep[x]+w[x]]-p1+hsh2[zero+dep[x]-w[x]]-p2; } int main(){ freopen("running.in","r",stdin); freopen("running.out","w",stdout); n=_read();m=_read(); for(int i=1,x,y;i<n;i++)x=_read(),y=_read(),add(x,y),add(y,x); for(int i=1;i<=n;i++)w[i]=_read(); work(); for(int i=1;i<=m;i++){ int u=_read(),v=_read(),lca=LCA(u,v); ans[lca]+=(w[lca]+dep[lca]==dep[u]); _add(0,u,dep[u],1);_add(0,lca,dep[u],-1); _add(1,v,2*dep[lca]-dep[u],1);_add(1,lca,2*dep[lca]-dep[u],-1); } memset(vis,1,sizeof(vis)); dfs1(1); for(int i=1;i<=n;i++)printf("%d ",ans[i]); return 0; }