P1600 天天愛跑步 解題報告

2021-09-25 18:38:57 字數 3534 閱讀 4380

用的是一種很容易理解但寫起來卻有點小煩的差分。

演算法模型構建:

本題實際上求的是兩種情況:

1.在i節點下方的wi層有多少個 起點,

2.在i節點下方的有多少個滿足一定條件的終點。

情況1:

也就是在末節點的再上乙個節點打上乙個del標記。

而我們的答案就是此節點的子樹的起點集群中:

dep[j]=dep[i]+w[i] 的j節點的個數

即cnt1[ dep[i]+w[i] ]

此節點中終點集群中:

dep[j]-dep[i]=len-w[i]的節點j的個數

化簡得: dep[j]-len=dep[i]-w[i]

對於終點型別的資料,我們儲存他的dep[j]-len

則答案為cnt2[ dep[i]-w[i] ]

最後再減去該子樹中的del1和del2

如何處理子樹資訊:

將每個節點進入時的cnt1,cnt2,del1,del2資訊記錄

在掃瞄完他的子樹後的cnt1,cnt2,del1,del2相減

即可得到子樹上的cnt1,cnt2,del1,del2

但因為乙個節點只會關心cnt1,del1的dep[i]+w[i]

和cnt2,del2的dep[i]-w[i]

所以只要維護這些值的資訊就行了。

嘟嘟嚕,提到現在已經基本上做完了,最後乙個問題,如何儲存那些資訊,將每個起點、終點、lca的資訊放到每個節點是不現實的(空間要開n^2),所以我們可以先將他們全部儲存下來,再按照他們離開dfs搜尋棧時的時間戳排序,之後用while把排完序的陣列的尾部依次取出即可。

#include

#define for(i,a,b) for(int i=a;i<=b;++i)

#define ll long long

#define maxn 300000

#define ban 300000

#define il inline

using

namespace std;

intread()

while

(isdigit

(c))

return flag*x;

}int n,m;

struct graph_typeline[maxn*2+

10];int head[maxn+10]

, tot=0;

il void

add(

int a,

int b)

; head[a]

=tot;

line[

++tot]

=(line)

; head[b]

=tot;

}int fa[maxn+10]

[30], dep[maxn+10]

;int dfn[maxn+10]

, cnt=0;

int ord[maxn+10]

,cnt2=0;

il void

dfs1

(int now,

int f)

ord[now]

=++cnt2;

} il int

lca(

int x,

int y)

}s;int t[maxn+10]

;int data[maxn+10]

;struct chg_typeadd1[maxn+10]

,add2[maxn+10]

,del1[maxn+10]

,del2[maxn+10]

;int cnt;

bool

cmp(chg_type a,chg_type b)

int ans[maxn+10]

;int a1[maxn+10]

,a2[

2*maxn+10]

,d1[maxn+10]

,d2[

2*maxn+10]

;int na1,na2,nd1,nd2;

il void

dfs(

int now)

while

( na1<=cnt && add1[na1]

.id==now )

a1[add1[na1]

.data]

++, na1++

;while

( na2<=cnt && add2[na2]

.id==now )

a2[add2[na2]

.data]

++, na2++

;while

( nd1<=cnt && del1[nd1]

.id==now )

d1[del1[nd1]

.data]

++, nd1++

;while

( nd2<=cnt && del2[nd2]

.id==now )

d2[del2[nd2]

.data]

++, nd2++

;

ans[now]

=( a1[data1]

-ta1 )

+( a2[data2]

-ta2 )

-( d1[data1]

-td1 )

-( d2[data2]

-td2 )

;return;}

intmain()

s.dfs1(1

,0);

for(i,

1,n) t[i]

=read()

;for

(i,1

,m)sort

(add1+

1,add1+cnt+

1,cmp)

;sort

(add2+

1,add2+cnt+

1,cmp)

;sort

(del1+

1,del1+cnt+

1,cmp)

;sort

(del2+

1,del2+cnt+

1,cmp)

; na1=

1; na2=

1; nd1=

1; nd2=1;

s.ord[0]

=n+10

;dfs(1

);for(i,

1,n) cout<

<<

" ";

return0;

}```

洛谷 P1600 天天愛跑步

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

洛谷P1600 天天愛跑步

樹上的題有點意思啊 這題我最開始的想法是 固定乙個觀測點i,能觀測到的一定是起點距離 i 為 w i 的點的子集合。問題是這個集合只有一部分的點會經過 i 點,就很煩。於是得換個思路,不放固定乙個跑步的人 x 觀察他對哪些觀察者 i 產生了貢獻。於是我們得到了公式,對於上公升階段的點來說,不妨設 s...

P1600 天天愛跑步 線段樹合併 lca

天天愛跑步 有點毒瘤的題目 我們觀察一下性質 在路徑 u,v 上 如果這個路徑 對某個點j 有貢獻j肯定得在路徑 u,v上 也就是說 如果當前遍歷的點在 lca u,v 上方的話 那麼 u,v路徑是肯定沒有貢獻的 因此到達lca時我們要把貢獻去掉 在說怎麼算貢獻 設u為起始點 v為終止點 如果u對於...