NOIP2016 天天愛跑步(線段樹 桶)

2022-05-09 14:39:12 字數 3348 閱讀 1473

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

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

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

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

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

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

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

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

接下來 m行,每行兩個整數si和ti,表示乙個玩家的起點和終點。

對於所有的資料,保證 。1<=si,ti<=n,0<=wj<=n

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

首先,我們考慮什麼樣的玩家可以被看到,那麼就是說什麼時候玩家到某個節點。

設玩家初始時的位置深度為d。

大概是這樣的,如果玩家在向上走時,會在0時刻走到路徑上d深度的點,在1時刻走到d-1深度的點,2時刻走到d-2是深度的點。

那麼在向上走的過程中,所經過點上的觀察員只要使等式(w+deep)=d成立,就能看見玩家。

同樣的在玩家向下走的時候也有類似的結論,就是只要玩家的(d-2*路徑上lca的deep)=(w-deep)就能看見。

那麼我們開線段樹存就好了。

對於每個深度開兩棵線段樹分別記錄向下走的和向上走的某個節點觀察的值,也就是說在每乙個玩家的d上的線段樹對於每個玩家經歷的點上統一+1。

也就是說樹鏈上+1,這樣只要單點查詢就可以了。

樹鏈上+1,使用樹鏈剖分就好了^_^

線段樹佔很大空間動態開點就好了

還有對於一些深度較淺的點我們發現最終的向下行走的索引可能為負,所以對於向下走的線段樹索引統一加上3e5

大概就是這樣了^_^

1 #include2 #include3 #include4

using

namespace

std;

5const

int n=(int

)(3e5);

6const

int t=(int)(13000000);7

struct

pntp[n];

17struct

ente[n*2

];21

struct

sgttr[t];

26int

cnt;

27int

siz;

28int

dnt;

29int

n,m;

30int rtu[n*2],rtd[n*2

];31

intsta[n],fin[n];

32void ade(int f,int

t)33

39void tr_pushdown(int

spc)

4047

void tr_build(int &spc,int l,int r,int

plc)

4859

60void tr_add(int ll,int rr,int l,int r,int spc,int

v)61

71int mid=(l+r)/2;72

tr_add(ll,rr,l,mid,tr[spc].ls,v);

73 tr_add(ll,rr,mid+1

,r,tr[spc].rs,v);

74return;75

}76int tr_val(int plc,int spc,int l,int

r)77

89void basic_dfs(int x,int

f)90

107}

108}

109void build_dfs(int x,int

tp)110

125}

126int lca(int x,int

y)127

134if(p[x].dp>p[y].dp)

135swap(x,y);

136return

x;137

}138

intmain()

139148

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

149 scanf("

%d",&p[i].w);

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

151 scanf("

%d%d

",&sta[i],&fin[i]);

152 basic_dfs(1,1

);153 build_dfs(1,1

);154

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

155164 tr_add(p[f].ind,p[x].ind,1,n,rtu[rt],1

);165 tr_add(p[f].ind,p[f].ind,1,n,rtu[rt],-1

);166 x=fin[i];

167 rt=n+p[sta[i]].dp-2*p[f].dp;

168while(p[x].top!=p[f].top)

169173 tr_add(p[f].ind,p[x].ind,1,n,rtd[rt],1

);174

}175

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

176181

return0;

182 }

NOIP2016 天天愛跑步

時間限制 2 s 記憶體限制 512 mb 題目描述 小c同學認為跑步非常有趣,於是決定製作一款叫做 天天愛跑步 的遊戲。天天愛跑步 是乙個養成類遊戲,需要玩家每天按時上線,完成打卡任務。這個遊戲的地圖可以看作一棵包含n個結點和n 1條邊的樹,每條邊連線兩個結點,且任意兩個結點存在一條路徑互相可達。...

NOIP2016天天愛跑步

小c同學認為跑步非常有趣,於是決定製作一款叫做 天天愛跑步 的遊戲。天天愛跑步 是乙個養成類遊戲,需要玩家每天按時上線,完成打卡任務。這個遊戲的地圖可以看作一一棵包含 nn n個結點和 n 1n 1n 1條邊的樹,每條邊連線兩個結點,且任意兩個結點存在一條路徑互相可達。樹上結點編號為從11 1到nn...

NOIP2016 天天愛跑步

看這道題不爽很久了,但一直沒有開它,原因是我不會 我太菜了 看了題解還是寫不來,因為我不會線段樹合併。然後今天學了dsu on tree這種神奇的科技,成功把它a了,效率吊打線段樹合併。於是寫篇題解紀念一下。洛谷p1600 天天愛跑步 不帶修改的樹上路徑資訊的維護,很容易想到樹上差分。我們考慮一條路...