洛谷P1600 天天愛跑步(線段樹合併)

2022-05-05 20:42:07 字數 4502 閱讀 4179

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

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

現在有mm個玩家,第ii個玩家的起點為 s_is

i

​ ,終點為 t_it

i

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

小c想知道遊戲的活躍度, 所以在每個結點上都放置了乙個觀察員。 在結點jj的觀察員會選擇在第w_jw

j

​ 秒觀察玩家, 乙個玩家能被這個觀察員觀察到當且僅當該玩家在第w_jw

j

​ 秒也理到達了結點 jj 。 小c想知道每個觀察員會觀察到多少人?

注意: 我們認為乙個玩家到達自己的終點後該玩家就會結束遊戲, 他不能等待一 段時間後再被觀察員觀察到。 即對於把結點jj作為終點的玩家: 若他在第w_jw

j

​ 秒前到達終點,則在結點jj的觀察員不能觀察到該玩家;若他正好在第w_jw

j

​ 秒到達終點,則在結點jj的觀察員可以觀察到這個玩家。

輸入輸出格式

輸入格式:

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

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

接下來一行 nn個整數,其中第jj個整數為w_jw

j

​ , 表示結點jj出現觀察員的時間。

接下來 mm行,每行兩個整數s_is

i

​ ,和t_it

i

​ ,表示乙個玩家的起點和終點。

對於所有的資料,保證1\leq s_i,t_i\leq n, 0\leq w_j\leq n1≤s

i

​ ,t

i

​ ≤n,0≤w

j

​ ≤n 。

輸出格式:

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

輸入輸出樣例

輸入樣例#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

題解:號稱提高組最難一題,其實難度還行

考慮把乙個人的路徑拆成起點到lca和lca到終點兩段,差分一下用線段樹維護

具體的操作是對起點插入deep起點,終點插入2*deep[lca]-deep起點,相當於把起點沿lca翻上去。

然後線段樹合併一波就搞定了

查詢的是每個點deep+wj和deep-wj距離的點有幾個

其實線段樹合併是大材小用了,如果對每個點查詢li-ri時間之間有多少人經過顯然才更妙

**如下:

#include#define lson tr[now].l

#define rson tr[now].r

using

namespace

std;

struct

tree

tr[20000010

];vector

g[300010

];vector

op1[300010],op2[300010

];int n,m,ans[300010],q[300010],rt[300010],deep[300010],fa[300010][20

],cnt;

int n=600000

;int dfs(int now,int f,int

dep)

for(int i=0; i)

}int lca(int x,int

y)

if(x==y) return

x;

for(int i=19; i>=0; i--)

return fa[x][0];}

int push_up(int

now)

int insert(int &now,int l,int r,int pos,int

val)

int mid=(l+r)>>1

;

if(pos<=mid)

else

push_up(now);

}int query(int now,int l,int r,int

pos)

int merge(int a,int b,int l,int

r)

int mid=(l+r)>>1

; tr[a].l=merge(tr[a].l,tr[b].l,l,mid);

tr[a].r=merge(tr[a].r,tr[b].r,mid+1

,r);

push_up(a);

returna;}

int solve(int now,int

f)

for(int i=0; i)

for(int i=0; i)

if(deep[now]+n-q[now]>=0) ans[now]+=query(rt[now],0,n,deep[now]+n-q[now]);

if(deep[now]+n+q[now]<=n&&q[now]!=0) ans[now]+=query(rt[now],0,n,deep[now]+n+q[now]);

}int

main()

for(int i=1; i<=n; i++) scanf("

%d",&q[i]);

dfs(

1,0,1

);

for(int i=1; i<=m; i++)

solve(

1,0);

for(int i=1;i<=n;i++)

}

洛谷 P1600 天天愛跑步

題面就不貼上了 把每個玩家的路徑拆成一條到lca的路徑和從lca到終點的路徑 然後,使用樹上差分統計答案即可 那麼,樹上差分是什麼?差分的具體思想是,當某區間內某元素對答案有貢獻,就在區間起點打乙個 1 標記代表多出了乙個對答案有貢獻的元素,在終點打乙個 1標記代表乙個對答案有貢獻的元素在該位置 結...

洛谷P1600 天天愛跑步

樹上的題有點意思啊 這題我最開始的想法是 固定乙個觀測點i,能觀測到的一定是起點距離 i 為 w i 的點的子集合。問題是這個集合只有一部分的點會經過 i 點,就很煩。於是得換個思路,不放固定乙個跑步的人 x 觀察他對哪些觀察者 i 產生了貢獻。於是我們得到了公式,對於上公升階段的點來說,不妨設 s...

洛谷 P1600 天天愛跑步(LCA 亂搞)

傳送門 我們把每一條路徑拆成 u lca 和 lca v 的路徑 先考慮 u lca 如果這條路徑會對路徑上的某乙個點產生貢獻,那麼滿足 dep u dep x w x dep u dep x w x 注意到 dep x w x 是乙個定值,所以我們只要去找它的子樹裡有多少個點的 dep 等於 de...