天天愛跑步 (NOIP2016)LCA 桶

2021-09-26 07:31:10 字數 3832 閱讀 6005

並沒有參加noip2016,不過仍然聽說過這題的威名,今日一見,果然不凡操蛋

暴力分給的很足,但是暴力寫完了,離正解也不遠了

維護一下父親,每條路徑暴力跳就好了

對於鏈式,我們思考一下,怎麼跑才會產生貢獻

對於從上往下跑的,只有dis

t(st

arti

,x)=

=w

xdist(start_i,x)==w_x

dist(s

tart

i​,x

)==w

x​才可以被看到

稍微改改式子dep

x−de

psta

rti=

=wx—

——

—>de

psta

rti=

=dep

x−wx

dep_x-dep_==w_x————>dep_==dep_x-wx

depx​−

deps

tart

i​​=

=wx​

————

>de

psta

rti​

​==d

epx​

−wx那麼我們建立乙個桶,遇到路徑頭就b[d

epx−

wx]+

+b[dep_x-w_x]++

b[depx

​−wx

​]++

,遇到路徑尾就b[d

epx−

wx]−

−b[dep_x-w_x]--

b[depx

​−wx

​]−−

,這樣從上往下的路徑就好了,那麼從下往上呢,稍微推一推式子,就可以發現,這是產生貢獻的東西為dep

x+wx

dep_x+w_x

depx​+

wx​,我們我們遇到路徑尾++++

++,再在路徑頭−−--

−−,就可以解決這個問題

這個部分頭都是路徑頭都是1,也就是根,那麼直接統計,對於每個路徑,在樹上查分,如果這個觀察員的dep

x==w

x+

1dep_x==w_x+1

depx​=

=wx​

+1,答案就是差分統計的值,不然就是0

這裡牽扯到了乙個和正解有關的思想

我們思考一下,仍然使用桶的思想,但是會不會出現不合法的情況呢

如果兩個點(點a,點b)在一棵子樹中,他倆的父親是起點,路徑為fa—

—>

af_a——>a

fa​—

—>

a,但是如果資料巧妙,dep

x+wx

dep_x+w_x

depx​+

wx​也有可能滿足統計的情況,怎麼辦呢,我們從下往上統計,遇到路徑尾就往桶裡面扔,遇到頭就退掉,這樣,在return回這個點的時候,我們產生的差值就是這個點被經過的貢獻,正解也用到了這個思想。

終於到了滿分思想

首先,我們把路徑拆成兩條,乙個是u——

>lc

au——>lca

u——>lc

a,乙個是lca

——

>

vlca——>v

lca—

—>

v,對於往上的一條,我們使用乙個桶維護,詳情見40分思路,對於向下的,我們使用另外乙個桶,按照80分的思路維護,就可以得出結果

思路已經結束,但實際過程中會有很多問題

統計兩次lca導致答案變大

因為路徑拆成了兩條,所以統計兩次lca是有可能的,我們思考一下何種情況會產生

當且僅當dep

lcau

,v+w

lcau

,v==

depu

dep_}+w_}==dep_u

deplca

u,v​

​+wl

cau,

v​​=

=dep

u​,這種情況下,往上的一條,該路徑條件符合,會統計一次,在往下的一條,該點仍然符合條件,會被重複統計一次,這種情況,我們只需要在讀入路徑的時候先減一即可

桶2負下標問題

這個直接平移陣列即可

乙個點是多條路徑的起點(終點)怎麼辦

我們使用鏈式前向星的方式,存起來就行

說了這麼多,**仍然不怎麼好寫,細節看**吧

#include

#include

#include

#include

#include

#include

#include

using

namespace std;

const

int maxn =

3e5+7;

const

int n =25;

struct node

edge1[maxn*2]

,edge2[maxn*2]

,edge3[maxn*2]

;int cnt1,cnt2,cnt3,head1[maxn]

,head2[maxn]

,head3[maxn]

,ans[maxn]

;int n,m,w[maxn]

,dep[maxn]

,dis[maxn]

,len[maxn]

,be[maxn]

,en[maxn]

;int sum[maxn]

,b1[maxn*3]

,b2[maxn*3]

,f[maxn]

[n+2

],mi[n+2]

,mov=maxn;

void

add1

(int from,

int to)

void

add2

(int from,

int to)

void

add3

(int from,

int to)

void

dfs1

(int u)

}int

lca(

int a,

int b)

if(a==b)

return a;

for(

int i=

log(dep[a])/

log(2)

;i>=

0;i--)}

return f[a][0

];}void

dfs2

(int u)

b1[dep[u]]+

=sum[u]

;for

(int i=head2[u]

;i;i=edge2[i]

.next)

ans[u]

+=b1[dep[u]

+w[u]

]-t1+b2[w[u]

-dep[u]

+mov]

-t2;

for(

int i=head3[u]

;i;i=edge3[i]

.next)

}int

main()

for(

int i=

1;i<=n;i++

)scanf

("%d"

,&w[i]);

dfs1(1

);for(

int i=

1;i<=m;i++

)dfs2(1

);for(

int i=

1;i<=n;i++

)printf

("%d "

,ans[i]);

}

天天愛跑步NOIP

首先,我們考慮對於一條路徑 從x y,可以把它拆分成兩部分,圖中用虛線分開,然後這條路徑就變成了x lca,son lca y 先來考慮從x向上走到lca的路徑,對於這條路上的節點i,玩家能對節點i產生貢獻的前提是deep x w i deep i 移項可得deep x w i deep i 也就是...

NOIP2016 天天愛跑步

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

NOIP2016天天愛跑步

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