JZOJ4331 清華集訓模擬 樹

2021-09-26 14:17:24 字數 3736 閱讀 9760

給你一棵帶點權的樹,求將樹變成一堆不相交的鏈,而且這些鏈的權值和非負的方案數。

顯然這道題是個dpdp

dp。首先求個字首和sum

sumsu

m。為了後面講述方便,我這樣設:fi,

jf_

fi,j

​表示以i

ii為根的子樹,其中某條鏈從x

xx伸出到i

ii的方案數,而且sum

x=

jsum_x=j

sumx​=

j。還有設g

ig_i

gi​表示以i

ii為根的,沒有伸出去的鏈的方案數。

顯然有這樣的轉移:

∏ gi

→fx,

sumx

fy,j

∏i≠y

gi→f

x,

j\prod g_i\to f_\\ f_\prod_ g_i\to f_x,j

∏gi​→f

x,su

mx​​

fy,j

​i̸​

=y∏​

gi​→

fx​,jfx

,j→g

x(j−

sumf

ax≥0

)fy,

jfz,

k∏i≠

y,i≠

zgi→

gx(j

+k−2

sumx

+ax≥

0)

f_ \to g_x \ (j-sum_\geq 0)\\ f_f_\prod_g_i\to g_x \ (j+k-2sum_x+a_x\geq 0)

fx,j​→

gx​(

j−su

mfax

​​≥0

)fy,

j​fz

,k​i

̸​=y

,i̸​

=z∏​

gi​→

gx​(

j+k−

2sum

x​+a

x​≥0

)如果直接這樣搞肯定會**。所以考慮用線段樹來維護fff。

由於可能會出現g

gg值為0

00的情況,所以不能直接用逆元來搞。

要維護個字首積和字尾積。

首先要求出重兒子,把重兒子作為第乙個兒子,然後線段樹合併之前也啟發式合併。

具體來說,我們欽定j

j<

k。在合併的時候(設前面子樹合併出來的線段樹為a

aa,這個線段樹為b

bb)當前的兒子作為k

kk,遍歷b

bb的所有葉子節點,並在a

aa中區間詢問。這時候記得要乘上字尾積。將詢問出來的東西加在g

xg_x

gx​中。

然後兩個合併在一起。記得在合併之前,整個a

aa乘子樹的g

xg_x

gx​,整個b

bb乘字首積

。搞完這個再合併。

最後你就會愉快地發現,所有子樹合併之後就是上面第二行式子。這樣只需要把第一行的加進去。第四行的式子已經計算完了,只需要再加上第乙個式子就可以了。

然而這不是題解的做法,作為乙個小蒟蒻,表示看不懂題解……

using

namespace std;

#include

#include

#include

#include

#define n 100010

#define inf 1000000000

#define mo 1000000007

int n;

int a[n]

;struct edge e[n*2]

;int ne;

edge *last[n]

;struct node *null;

struct node

inline

void

pushdown()

}inline

void

update()

} d[n*40]

;int cnt;

node *rt[n]

;inline node *

newnode()

);}void

add(node *

&t,int l,

int r,

int x,

int c)

t->

pushdown()

;int mid=l+r>>1;

if(x<=mid)

add(t-

>l,l,mid,x,c)

;else

add(t-

>r,mid+

1,r,x,c)

; t-

>

update()

;}intquery

(node *t,

int l,

int r,

int st,

int en)

node *

merge

(node *a,node *b)

intcalc

(node *t,

int l,

int r,node *rt,

int bor)

int fa[n]

,sum[n]

,siz[n]

,hs[n]

;int son[n]

,ns,pre[n]

,suc[n]

;int g[n]

;voiddp(

int x)if(

!hs[x]

) son[ns=1]

=hs[x]

; pre[0]

=1,pre[1]

=g[hs[x]];

for(edge *ei=last[x]

;ei;ei=ei-

>las)

if(ei-

>to!=fa[x]

&& ei-

>to!=hs[x]

) suc[ns+1]

=1;for

(int i=ns;i>=1;

--i)

suc[i]=(

long

long

)suc[i+1]

*g[son[i]

]%mo;

rt[x]

=rt[hs[x]];

for(

int i=

2;i<=ns;

++i)

add(rt[x]

,-inf,inf,sum[x]

,pre[ns]);

g[x]+=

query

(rt[x]

,-inf,inf,sum[fa[x]

],inf)

; g[x]

>=mo?g[x]

-=mo:0;

}int

main()

; last[u]

=e+ne++

; e[ne]=;

last[v]

=e+ne++;}

null=d;

*null=;dp

(n>>1)

;printf

("%d\n"

,g[n>>1]

);return0;

}

好多樹形dp都可以用線段樹合併來優化啊……

JZOJ4331 清華集訓模擬 樹

給你一棵帶點權的樹,求將樹變成一堆不相交的鏈,而且這些鏈的權值和非負的方案數。顯然這道題是個 dp 首先求個字首和 sum 為了後面講述方便,我這樣設 f 表示以 i 為根的子樹,其中某條鏈從 x 伸出到 i 的方案數,而且 sum x j 還有設 g i 表示以 i 為根的,沒有伸出去的鏈的方案數...

JZOJ 4330 清華集訓模擬 幾何題

這題的複雜度是o 106log 106 的fft 害怕.jpg 預處理cnt i j k 表示x,y,z的差為i,j,k的有多少對,首先,xi xj可以變成xi mx xj mx為最大xi 那麼每位都是非負數了,現在要做3維的多項式乘法,考慮用2mx進製儲存這三個數,壓在一起變乙個數,這樣就把3維變...

JZOJ4330 清華集訓模擬 幾何題

也懶得解釋題目大意了 正解居然是fft fftff t?不要看題目的那個式子這麼長,也不要在那個式子上下手。其實我們會發現,不同的 xi xj,yi y j,zi zj x i x j,y i y j,z i z j xi x j y i y j z i z j 並不多。如果我們求出每個三元組的出現...