小c同學認為跑步非常有趣,於是決定製作一款叫做《天天愛跑步》的遊戲。«天天愛跑步»是乙個養成類遊戲,需要玩家每天按時上線,完成打卡任務。
這個遊戲的地圖可以看作一一棵包含 nn
n個結點和 n−1n-1n−
1條邊的樹, 每條邊連線兩個結點,且任意兩個結點存在一條路徑互相可達。樹上結點編號為從11
1到nn
n的連續正整數。
現在有mm
m個玩家,第ii
i個玩家的起點為 sis_isi
,終點為 tit_iti
。每天打卡任務開始時,所有玩家在第00
0秒同時從自己的起點出發, 以每秒跑一條邊的速度, 不間斷地沿著最短路徑向著自己的終點跑去, 跑到終點後該玩家就算完成了打卡任務。 (由於地圖是一棵樹, 所以每個人的路徑是唯一的)
小c想知道遊戲的活躍度, 所以在每個結點上都放置了乙個觀察員。 在結點jj
j的觀察員會選擇在第wjw_jwj
秒觀察玩家, 乙個玩家能被這個觀察員觀察到當且僅當該玩家在第wjw_jwj
秒也理到達了結點 jj
j 。 小c想知道每個觀察員會觀察到多少人?
注意: 我們認為乙個玩家到達自己的終點後該玩家就會結束遊戲, 他不能等待一 段時間後再被觀察員觀察到。 即對於把結點jj
j作為終點的玩家: 若他在第wjw_jwj
秒前到達終點,則在結點jj
j的觀察員不能觀察到該玩家;若他正好在第wjw_jwj
秒到達終點,則在結點jj
j的觀察員可以觀察到這個玩家。
輸入格式:
第一行有兩個整數nn
n和mm
m 。其中nn
n代表樹的結點數量, 同時也是觀察員的數量, mm
m代表玩家的數量。
接下來 n−1n- 1n−
1行每行兩個整數uu
u和 vv
v,表示結點 uu
u到結點 vv
v有一條邊。
接下來一行 nn
n個整數,其中第jj
j個整數為wjw_jwj
, 表示結點jj
j出現觀察員的時間。
接下來 mm
m行,每行兩個整數sis_isi
,和tit_iti
,表示乙個玩家的起點和終點。
對於所有的資料,保證1≤si,ti≤n,0≤wj≤n1\leq s_i,t_i\leq n, 0\leq w_j\leq n1≤
si,
ti≤
n,0≤
wj≤
n 。輸出格式:
輸出1行 nn
n個整數,第jj
j個整數表示結點jj
j的觀察員可以觀察到多少人。
輸入樣例#1:複製
6 32 31 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 31 2
2 3
2 4
1 5
0 1 0 3 0
3 1
1 45 5
輸出樣例#2:複製
1 2 1 0 1
【樣例1說明】
對於1號點,wi=0w_i=0wi
=0,故只有起點為1號點的玩家才會被觀察到,所以玩家1和玩家2被觀察到,共有2人被觀察到。
對於2號點,沒有玩家在第2秒時在此結點,共0人被觀察到。
對於3號點,沒有玩家在第5秒時在此結點,共0人被觀察到。
對於4號點,玩家1被觀察到,共1人被觀察到。
對於5號點,玩家1被觀察到,共1人被觀察到。
對於6號點,玩家3被觀察到,共1人被觀察到。
【子任務】
a[x]表示x點的觀察員觀察時間,s為起點,t為終點
以下是所有部分分(80分)
1.直接爆搜,複雜度o(nm)
2.對於一條鏈的情況:對於點x,只有可能從x+a[x] or x-a[x] 為起點的路線且要求終點穿過該點,把每一條路線的方向(正負),長度(t-s)用vector壓入起點位置,再o(n)掃一遍即可.複雜度應該大約可能為o(n+m)左右
3.對於起點為1的情況,以1為根的樹節點的深度就是到達的時間,如果dep[x]=a[x]則該點的答案為其子樹內終點的個數.複雜度:o(n)
4.對於終點為1的情況:以1為根的樹節點的答案就是子樹內深度為dep[x]+a[x]的終點個數,若對於每乙個點都搜尋一遍子樹,複雜度為o(n^2),但每一次最後去搜重兒子,把重兒子的桶直接利用,(就是樹上啟發式合併)複雜度為o(n lgn)
#includeusing namespace std;
#define for(i,a,b) for(register int i=(a);i<=(b);++i)
#define pb push_back
const int maxx=3e5+5;
int read()
int be[maxx],ne[maxx<<1],to[maxx<<1],e=0,a[maxx],n,m;
struct nodeplay[maxx];
void add(int x,int y)
// pts:25
bool flag;
int end,ans[maxx];
void dfs(int id,int fa,int ti)
for(int i=be[id];i;i=ne[i])
}void solve1()
for(i,1,n) printf("%d ",ans[i]);
}// pts:15
vectorq[100000];
void solve2()
int pos,ans;
for(i,1,n)
if(pos>0)
for(int j=0;j=a[i]) ++ans;
pos=i+a[i];
if(pos<=n)
for(int j=0;jsize[son[id]]) son[id]=go; }}
void change(int x,int f,int dep,int k)
void bfs(int id,int fa,int dep,bool keep)
change(id,fa,dep,1);
if(dep+a[id]<=n) ans[id]+=size[dep+a[id]];
if(son[id]) skip[son[id]]=0;
if(!keep) change(id,fa,dep,-1);
}void solve4()
int main()
for(i,1,n) a[i]=read();
for(i,1,m) play[i].s=read(), play[i].t=read();
if(n<=1000) solve1();
else if(n%10==4) solve2();
else if(n%10==5) solve3();
else if(n%10==6) solve4();
return 0;
}
對於暴力的第四檔已經就是正解做法了,再結合樹上差分一下就可以a了.
對於點x可以計入貢獻的情況:
dep[x]+a[x]=dep[s]
dep[s]-a[x]=2*dep[lca(s,t)]-dep[s]
再隨便打幾個標記,開個桶維護一下,注意再lca處要加乙個減的標記,lca的父親處也加乙個減的標記,這樣既可以算到lca處的答案,也不會算兩遍,dfs時繼續啟發式合併.複雜度(n lgn)
#includeusing namespace std;
#define for(i,a,b) for(register int i=(a);i<=(b);++i)
#define pb push_back
const int maxx=3e5+5,n=1e6;
int read()
int be[maxx],ne[maxx<<1],to[maxx<<1],a[maxx],n,m,e=0,ans[maxx];
int size[maxx],son[maxx],fa[maxx],dep[maxx],jump[maxx],skip[maxx],tong[20000000];
struct node;
vectorq[maxx];
inline void add(int x,int y)
inline void dfs_init(int id)
}inline void dfs_jump(int id,int top)
int lca(int x,int y)
inline void change(int id,int fa,bool k){
for(int i=0;i
NOIP2016 天天愛跑步
時間限制 2 s 記憶體限制 512 mb 題目描述 小c同學認為跑步非常有趣,於是決定製作一款叫做 天天愛跑步 的遊戲。天天愛跑步 是乙個養成類遊戲,需要玩家每天按時上線,完成打卡任務。這個遊戲的地圖可以看作一棵包含n個結點和n 1條邊的樹,每條邊連線兩個結點,且任意兩個結點存在一條路徑互相可達。...
NOIP2016 天天愛跑步
看這道題不爽很久了,但一直沒有開它,原因是我不會 我太菜了 看了題解還是寫不來,因為我不會線段樹合併。然後今天學了dsu on tree這種神奇的科技,成功把它a了,效率吊打線段樹合併。於是寫篇題解紀念一下。洛谷p1600 天天愛跑步 不帶修改的樹上路徑資訊的維護,很容易想到樹上差分。我們考慮一條路...
NOIP2016天天愛跑步
時間限制 2 s 記憶體限制 512 mb 小c同學認為跑步非常有趣,於是決定製作一款叫做 天天愛跑步 的遊戲。天天愛跑步 是乙個養成類遊戲,需要玩家每天按時上線,完成打卡任務。這個遊戲的地圖可以看作一棵包含n個結點和n 1條邊的樹,每條邊連線兩個結點,且任意兩個結點存在一條路徑互相可達。樹上結點編...