小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...