用的是一種很容易理解但寫起來卻有點小煩的差分。
演算法模型構建:
本題實際上求的是兩種情況:
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對於...