[NOIP2016]天天愛跑步(lca+亂搞)

2557. [NOIP2016]天天愛跑步

時間限制:2 s   內存限制:512 MB

【題目描述】


小C同學認爲跑步非常有趣,於是決定製作一款叫做《天天愛跑步》的遊戲。《天天愛跑步》是一個養成類遊戲,需要玩家每天按時上線,完成打卡任務。

   這個遊戲的地圖可以看作一棵包含n個結點和n-1條邊的樹,每條邊連接兩個結點,且任意兩個結點存在一條路徑互相可達。樹上結點編號爲從1到n的連續正整數。

   現在有m個玩家,第i個玩家的起點爲Si,終點爲Ti。每天打卡任務開始時,所有玩家在第0秒同時從自己的起點出發,以每秒跑一條邊的速度,不間斷地沿着最短路徑向着自己的終點跑去,跑到終點後該玩家就算完成了打卡任務。(由於地圖是一棵樹,所以每個人的路徑是唯一的)

    小C想知道遊戲的活躍度, 所以在每個結點上都放置了一個觀察員。 在結點的觀察員會選擇在第Wj秒觀察玩家, 一個玩家能被這個觀察員觀察到當且僅當該玩家在第Wj秒也理到達了結點J  。 小C想知道每個觀察員會觀察到多少人?

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


【輸入格式】


第一行有兩個整數n和m。其中n代表樹的結點數量,同時也是觀察員的數量,m代表玩家的數量。

接下來n-1行每行兩個整數u和v,表示結點u到結點v有一條邊。

接下來一行n個整數,其中第j個整數爲Wj,表示結點j出現觀察員的時間。

接下來m行,每行兩個整數Si和Ti,表示一個玩家的起點和終點。

對於所有的數據,保證1≤Si,Ti≤n,0≤ Wj ≤n。


【輸出格式】

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

【樣例1輸入】

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

【樣例1輸出】

2 0 0 1 1 1

【樣例2輸入】

5 3

1 2

2 3

2 4

1 5

0 1 0 3 0

3 1

1 4

5 5

【樣例2輸出】

1 2 1 0 1

【提示】


對於1號點,W1=0,故只有起點爲1號點的玩家纔會被觀察到,所以玩家1和玩家2被觀察到,共2人被觀察到。

   對於2號點,沒有玩家在第2秒時在此結點,共0人被觀察到。

   對於3號點,沒有玩家在第5秒時在此結點,共0人被觀察到。

   對於4號點,玩家1被觀察到,共1人被觀察到。

   對於5號點,玩家2被觀察到,共1人被觀察到。

   對於6號點,玩家3被觀察到,共1人被觀察到。


剛看題時一臉懵逼。。。

(定義:dp[]數組爲深度數組,tim[]爲題中給出的w[])

首先把路徑分兩部分考慮,第一部分是從S(起點)到lca,第二部分是從lca到T(終點)。如果一個人被i處檢測到,有兩種情況:

1. 在第一部分被檢測到,當且僅當dp[i]+tim[i]=dp[S];

2. 在第二部分被檢測到,當且僅當len-(dp[T]-dp[i])=tim[i],即dp[i]-tim[i]=dp[T]-len;(len爲S到T的距離)

樹剖可能會被卡,所以我們用一種桶的方法:

首先考慮一個點在它的lca之上是沒有影響的(不會走深度小於lca的點)

維護兩個桶:一個起點的桶up和一個終點的桶dn(維護的值);

設當前節點爲i

那麼在以i爲起點的起點的桶中加"1"(具體實現是統計了一個A數組記錄從每個點出發的人的個數),換成人話,就是up[dp[i]]+=A[i];

同理,在以i爲終點的桶中加1,(注意:桶中維護的是值,所以換成人話是dn[dp[i]-len]++,結合上面式子看)

當回溯到lca時,自然要消除以它爲lca的點得影響,所以dfs回來時,將以i爲lca的起點的up減1,(人話:up[x]--,x爲以i爲lca的起點),以i爲lca的終點的dn減1,(人話:dn[x]--,x爲以i爲lca的終點)

那麼ans呢?ans[i]=up[dp[i]+w[i]]+dn[dp[i]-w[i]];(即對應的能產生貢獻的桶)

如果是一條鏈,可能會算重,所以我們用差值計算就好了.

兩遍dfs,複雜度O(n+m)

注意:dp[i]-len會出負數,可以統一加上300000來處理。

代碼:

#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 300005
#define maxx 600010
#define MA 300000
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
inline int read()
{   char c=getchar();int x=0,y=1;
    while(c<'0'||c>'9'){if(c=='-') y=-1;c=getchar();}
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*y;
}
int n,m,ans[maxn],fans[maxn],dp[maxn],ance[maxn],f[maxn],hea[maxn],nhea[maxn],num,nnum,num1,num2,num3,A[maxn];
int hea1[maxx],hea2[maxx],hea3[maxx],tim[maxn],dn[maxx],upp[maxx];
bool ok[maxn];
inline int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
inline void unionn(int x,int y){int a=find(x),b=find(y);if(a!=b) f[a]=b;}
struct road{int en,nex;}ro[maxx],ro1[maxx],ro2[maxx],ro3[maxx];
struct query{int en,nex,id;}qu[maxx];
inline void addq(int x,int y,int z)
{	qu[nnum].en=y;qu[nnum].id=z;qu[nnum].nex=nhea[x];nhea[x]=nnum++;
	qu[nnum].en=x;qu[nnum].id=z;qu[nnum].nex=nhea[y];nhea[y]=nnum++;
}
inline void add(int x,int y){ro[num].en=y;ro[num].nex=hea[x];hea[x]=num++;}
inline void add1(int x,int y){ro1[num1].en=y;ro1[num1].nex=hea1[x];hea1[x]=num1++;}
inline void add2(int x,int y){ro2[num2].en=y;ro2[num2].nex=hea2[x];hea2[x]=num2++;}
inline void add3(int x,int y){ro3[num3].en=y;ro3[num3].nex=hea3[x];hea3[x]=num3++;}
void lca(int x,int y)
{	ance[x]=x;ok[x]=1;dp[x]=dp[y]+1;
	for(int i=hea[x];i!=-1;i=ro[i].nex)
	{	int v=ro[i].en;if(ok[v]) continue;
		lca(v,x);unionn(x,v);ance[find(x)]=x;
	}
	for(int i=nhea[x];i!=-1;i=qu[i].nex)
	{	int v=qu[i].en;
		if(ok[v]) ans[qu[i].id]=ance[find(v)];
	}
}
void dfs(int x,int y)
{	int t1=upp[dp[x]+tim[x]],t2=dn[dp[x]-tim[x]+MA];upp[dp[x]]+=A[x];
	for(int i=hea1[x];i!=-1;i=ro1[i].nex) ++dn[ro1[i].en+MA];
	for(int i=hea[x];i!=-1;i=ro[i].nex) if(ro[i].en!=y) dfs(ro[i].en,x);
	fans[x]=upp[dp[x]+tim[x]]+dn[dp[x]-tim[x]+MA]-t1-t2;
	for(int i=hea2[x];i!=-1;i=ro2[i].nex){int v=ro2[i].en;--upp[v];if(v==dp[x]+tim[x]) --fans[x];}
	for(int i=hea3[x];i!=-1;i=ro3[i].nex) --dn[ro3[i].en+MA];
}
void init()
{	mem(hea,-1);mem(nhea,-1);mem(hea1,-1);mem(hea2,-1);mem(hea3,-1);
	for(int i=1;i<=n;++i) f[i]=i;
}
int main()
{   //freopen("runninga.in","r",stdin);
	//freopen("runninga.out","w",stdout);
    int __size__=64<<20;//擴棧用的 
	char *__p__=(char*)malloc(__size__)+__size__;
	__asm__("movl %0, %%esp\n"::"r"(__p__));
	n=read();m=read();
	init();int x,y;
	for(int i=1;i<n;++i) x=read(),y=read(),add(x,y),add(y,x);
	for(int i=1;i<=n;++i) tim[i]=read();
	for(int i=0;i<m;++i) x=read(),y=read(),addq(x,y,i);
	lca(1,0);int fr,to,tmp1,tmp2;
	for(int i=0;i<m;++i)
	{	to=qu[i<<1].en;fr=qu[i<<1|1].en;
		tmp1=dp[fr]+dp[to]-(dp[ans[i]]<<1);tmp2=dp[to]-tmp1;
		++A[fr];add1(to,tmp2);add2(ans[i],dp[fr]);add3(ans[i],tmp2);
	}
	dfs(1,0);printf("%d",fans[1]);
	for(int i=2;i<=n;++i) printf(" %d",fans[i]);
	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章