原題鏈結
題目大意
有一棵 n
nn 個節點的有根樹,有 m
mm 組詢問
每次詢問給出 a,k
a,ka,
k,求有多少個三元組 (a,
b,c)
(a,b,c)
(a,b,c
),滿足 a,b
a,ba,
b 都是 c
cc 的祖先,並且 a,b
a,ba,
b 之間的距離不超過 kkk
剛開始沒有思路,看了題解的分類討論後發現可以很輕鬆地轉化為線段樹合併
解題思路
首先,定義 d
xd_x
dx 為點 x
xx 的深度(根節點的深度為 111)
s
xs_x
sx 為點 x
xx 的子樹大小(包括 xxx)
考慮兩種情況
b
bb 是 a
aa 的祖先:那麼 b
bb 就必須在 a
aa 到根的路徑上。可能方案數就是 min(
da−1
,k)×
(sa−
1)
\min (d_a-1,k) \times (s_a-1)
min(da
−1,
k)×(
sa−
1)b
bb 在 a
aa 的子樹中:方案數就是 ∑dx
∈[da
+1,d
a+k]
(sx−
1)
\sum\limits_ (s_x-1)
dx∈[d
a+1
,da
+k]∑
(sx
−1)
,注意這裡的 x
xx 必須在 a
aa 的子樹中。
我們可以用線段樹合併維護子樹資訊來實現操作2,最終的答案就是兩方案數之和
#include
#include
using
namespace std;
const
long
long maxn=
300000+10
,inf=
0x3f3f3f3f
;const
long
long maxm=
6000000+10
;long
long d[maxn]
,s[maxn]
;long
long nxt[maxn<<1]
,to[maxn<<1]
;long
long sum[maxm]
,ls[maxm]
,rs[maxm]
;long
long root[maxn]
,head[maxn]
;long
long n,m,idcnt,edgecnt=1;
inline
void
add(
long
long x,
long
long y)
inline
long
long
read()
while
(ch>=
'0'&& ch<=
'9')s=
(s<<3)
+(s<<1)
+(ch^48)
,ch=
getchar()
;return s*w;
}inline
void
push_up
(long
long x)
void
modify
(long
long
&x,long
long l,
long
long r,
long
long pos,
long
long v)
long
long mid=
(l+r)
>>1;
if(pos<=mid)
modify
(ls[x]
,l,mid,pos,v)
;else
modify
(rs[x]
,mid+
1,r,pos,v)
;push_up
(x);
}long
long
merge
(long
long x,
long
long y)
long
long
query
(long
long k,
long
long l,
long
long r,
long
long x,
long
long y)
void
dfs(
long
long x,
long
long fa)
modify
(root[x],1
,n,d[x]
,s[x]-1
);}int
main()
dfs(1,
0);while
(m--
)return0;
}
洛谷P3899 湖南集訓 談笑風生
設t 為一棵有根樹,我們做如下的定義 設a和b為t 中的兩個不同節點。如果a是b的祖先,那麼稱 a比b不知道 高明到 去了 設a 和 b 為 t 中的兩個不同節點。如果 a 與 b 在樹上的距離不超過某個給定 常數x,那麼稱 a 與b 談笑風生 給定一棵n個節點的有根樹t,節點的編號為1 到 n,根...
P3899 湖南集訓 談笑風生
傳送門 首先 a,b,c 肯定在一條鏈上。當 b 為 a 的祖先時,a 的子樹中所有與它不同的點都可以作為點 c 當 a 為 b 的祖先時,b 的子樹中所有與它不同的點都可以作為答案 我會說我以前根本沒寫過線段樹合併結果完全不知道錯在 麼 luogu judger enable o2 minamot...
P3899 湖南集訓 談笑風生
題目大意 n個節點的樹,q次查詢,每次查詢給出a,k求三元組的數量 a,b,c a,b,c 的定義為 a b均為c的祖先且距離 k 離線,啟發式合併線段樹,長鏈剖分當然都能過這題 這裡講講主席樹的做法 dfs序建樹 a為b的祖先時 查詢a子樹內深度 dep a k的節點的子樹和 b為a祖先時 乘法原...