NOIP2016天天愛跑步

2021-08-10 13:25:03 字數 4578 閱讀 6772

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

2 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 3 

1 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條邊的樹,每條邊連線兩個結點,且任意兩個結點存在一條路徑互相可達。樹上結點編...