luogu P1600 天天愛跑步 LCA

2022-08-03 06:33:14 字數 3390 閱讀 6546

[luogu]p1600

[noip 2016]天天愛跑步

題目描述

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

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

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

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

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

輸入輸出格式

輸入格式:

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

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

接下來一行n個整數,其中第j個整數為wj​ ,

表示結點j出現觀察員的時間。

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

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

輸出格式:

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

輸入輸出樣例

輸入樣例1#:

6 32 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 45 5

輸出樣例2#:

1 2 1 0 1 

說明【樣例1說明】

對於1號點,w_i=0wi​=0,故只有起點為1號點的玩家才會被觀察到,所以玩家1和玩家2被觀察到,共有2人被觀察到。

對於2號點,沒有玩家在第2秒時在此結點,共0人被觀察到。

對於3號點,沒有玩家在第5秒時在此結點,共0人被觀察到。

對於4號點,玩家1被觀察到,共1人被觀察到。

對於5號點,玩家1被觀察到,共1人被觀察到。

對於6號點,玩家3被觀察到,共1人被觀察到。

【子任務】

(圖還是來自luogu)

今天總算搞懂了noip2016 day1 t2 running。

根據ysy大佬的**,自己的理解,首先%%% ysy。

對於s,t,我們先求出lca(我用的是樹鏈剖分)。

s,t對答案貢獻:

當在x->lca

dep[i]+t[i]=dep[s]

當在lca->y

dep[y]+dep[x]-2*dep[lca]=t[i]+dep[y]-dep[i]<=>t[i]-dep[i]=dep[x]-2*dep[lca]

這樣我們就可以處理兩個陣列,乙個記加的,乙個記減的。

但是當然還有問題,有可能預處理在x打了標記,可實際這條路沒有經過i,結果在i的答案加了怎麼辦?

注意,每次的x,y,在處理完lca就沒有用了,所以多搞乙個減去這些的操作,消除對子樹以外的影響,但也有可能在子樹內對其他無用的節點產生影響,所以要在dfs到s,t時,把影響減掉。

還有陣列要開大點啊,我也不知道為什麼一開始覺得夠了還re…

這題真的折磨死蒟蒻了。

**:

1

lca3 #include4 #include5 #include6

using

namespace

std;

7 inline int

read();

8namespace

lyse[n*3

];14

bool sig[n*3

];15

int w[n*3],p1[n],p2[n],n1[n*3],n2[n*3

];16

int x[n<<1],y[n<<1

],t[n],ans[n],pre[n];

17int

son[n],siz[n],dep[n],top[n],fa[n];

18int

n,m,cnt;

19void add(int x,int

y)23

void dfs1(int node,int

deep)35}

36void dfs2(int node,int

tp)46}47

int lca(int x,int

y)55}56

void init(bool flag,bool up,int node,int

deep)

63else67}

68void dfs(int

node)

82 ans[node]-=x[t[node]+dep[node]]+y[t[node]-dep[node]+n];83}

84int

main()

91 dfs1(1,1),dfs2(1,1

);92

for(i=1;i<=n;i++) t[i]=read();

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

102 dfs(1

);103

for(i=1;i<=n;i++) printf("

%d "

,ans[i]);

104 puts(""

);105

}106

}107

intmain()

111 inline int

read()

118while(c>='

0'&&c<='

9') kk=kk*10+c-'

0',c=getchar();

119return kk*ff;

120 }

luogu P1600 天天愛跑步

傳送門 1a此題暴祭 下面記點 x 深度為 de x 某個時間點記為 w x 首先,每條路徑是可以拆成往上和往下兩條路徑的 對於往上的路徑,假設有個人往上跑,w y 在點 y 那麼如果能對點 x 的觀察員產生貢獻,當且僅當 w x de x w y de y 對於往下的路徑,假設有個人往下跑,w y...

luogu P1600 天天愛跑步

膜你賽t3考了這題的簡化版,當場自閉。就此把此類題目總結一下。膜你賽題意是這樣的 相當於這個題最後問每個運動員被多少個觀察員觀察到。我們考慮把每條路徑拆成向上和向下兩條,本質相同,我們以向上路徑舉例。發現路徑上滿足 dep s dep x x s 表示路徑起點 的 x 都可以獲得 1 的貢獻。我們差...

洛谷 P1600 天天愛跑步

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