WC2019 數樹 樹形DP 多項式exp

2022-03-25 17:17:14 字數 3431 閱讀 3253

有兩棵 \(n\) 個點的樹 \(t_1\) 和 \(t_2\)。

你要給每個點乙個權值嗎,要求每個點的權值為 \([1,y]\) 內的整數。

對於一條同時出現在兩棵樹上的邊,這條邊的兩個端點的值相同。

若 \(op=0\),則給你兩棵樹 \(t_1,t_2\),求方案數。

若 \(op=1\),則給你一棵樹 \(t_1\),求對於所有 \(n^\) 種 \(t_2\),方案數之和。

若 \(op=2\),則求對於所有的 \(t_1,t_2\),求方案數之和。

\(n\leq 100000\)

新建乙個圖 \(g\),把兩棵樹的公共邊加到 \(g\) 中。記 \(m\) 為兩棵樹的公共邊數量。那麼答案就是 \(y^\)。

令 \(z=y^\),那麼答案就變成了 \(y^nz^m\)。也就是說,每有一條相同的邊,方案的貢獻就要 \(\times z\)。

這個大家都會。

\[z^m=\sum_^m\binom(z-1)^i

\]那麼可以列舉乙個邊集 \(e\),計算有多少種生成樹包含 \(e\),然後把答案加上方案數 \(\times^\)。

記這 \(e\) 條邊形成了 \(m\) 個連通塊,這些連通塊的大小為 \(a_1,a_2,\ldots,a_m\),那麼貢獻就是

\[\begin

&^\sum_^md_i=2m-2}(m-2)!\prod_^m\frac}\\

=&^n^\prod_^ma_i\\

\end

\]\(\prod_^ma_i\) 可以看成是每個連通塊內選乙個點的方案數。這樣就可以dp了。

時間複雜度:\(o(n)\)

列舉兩棵樹的公共邊個數:

\[\begin

s_n&=\sum_^^\sum_^ia_j=n}\frac(\prod_^i\frac})(n^\prod_^ia_j)^2\\

&=\sum_^^\frac}\sum_^ia_j=n}\prod_^i\frac}\\

&=\sum_^^n^\sum_^ia_j=n}\prod_^i\binom^ja_k)-1}{}a_j^\\

\end

\]記 \(f_l=\sum_^^n^\sum_^ia_j=l}\prod_^i\binom^ja_k)-1}{}a_j^\)。

轉移時列舉最後一塊的大小,有:

\[f_i=\begin

1&,i=0\\

\sum_^i\frac}&,i>0

\end

\]直接dp是 \(o(n^2)\) 的。

記 \(g_i=\sum_\frac\),\(f(x)\) 為 \(f\) 的 egf,\(g(x)\) 為 \(g\) 的 ogf,那麼

\[\begin

xf'(x)&=f(x)g(x)\\

\frac&=\frac\\

\ln f(x)&=\int \frac\\

f(x)&=e^}

\end

\]直接多項式 exp 就好了。

答案為 \((z-1)^nn^f_n\)

時間複雜度:\(o(n\log n)\)

const ll p=998244353;

ll fp(ll a,ll b)

const int n=100010;

int n,op;

ll z,_z;

ll ans;

namespace solve0

int x,y;

for(int i=1;iy)

swap(x,y);

a[x][y]++;

} ans=1;

for(int i=1;iy)

swap(x,y);

if(a[x].count(y))

ans=ans*z%p;

} }}namespace solve1

} void solve()

int x,y;

for(int i=1;i>1]>>1)|(i&1?n>>1:0);

if(rev[i]>i)

swap(a[i],a[rev[i]]);

}for(int i=2;i<=n;i<<=1)

for(int j=0;j>1);

static ll a1[n],a2[n];

memset(a1,0,sizeof(a1[0])*(n<<1));

memset(a2,0,sizeof(a2[0])*(n<<1));

memcpy(a1,a,sizeof(a1[0])*n);

memcpy(a2,b,sizeof(a2[0])*(n>>1));

ntt(a1,n<<1,1);

ntt(a2,n<<1,1);

for(int i=0;i>1);

static ll a1[n],a2[n],a3[n];

memset(b+(n>>1),0,sizeof(b[0])*(n>>1));

ln(b,a3,n);

memset(a1,0,sizeof(a1[0])*n);

memset(a2,0,sizeof(a2[0])*n);

memcpy(a1,b,sizeof(a1[0])*(n>>1));

for(int i=0;i<(n>>1);i++)

a2[i]=a[(n>>1)+i]-a3[(n>>1)+i];

ntt(a1,n,1);

ntt(a2,n,1);

for(int i=0;i>1),a1,sizeof(a1[0])*(n>>1));

} }ll inv[n],fac[n],ifac[n];

ll f[n],g[n],w[n];

void solve()

z--;

ntt::init();

fac[0]=fac[1]=ifac[0]=ifac[1]=inv[1]=1;

for(int i=2;i<=n;i++)

ll ifacz=fp(z,p-2);

// f[0]=1;

// for(int i=1;i<=n;i++)

// w[i]=fp(i,i);

// for(int i=1;i<=n;i++)

// for(int j=1;j<=i;j++)

// f[i]=(f[i]+f[i-j]*fac[i-1]%p*ifac[i-j]%p*ifac[j-1]%p*n%p*n%p*w[j]%p*ifacz)%p;

for(int i=1;i<=n;i++)

g[i]=fp(i,i)*n%p*n%p*ifac[i-1]%p*ifacz%p*inv[i]%p;

int k=1;

while(k<=n)

k<<=1;

ntt::exp(g,f,k);

ans=f[n]*fac[n]%p*fp(z,n)%p*fp(n,p-1-4)%p; }}

int main()

THUWC2019遊記 WC2019場外vp記

菜雞zx聯賽炸了以後又去不了wc了。於是報了個thuwc來旅遊,成功成為 體驗選手 thu對於非正式選手的說法 然後真的成了體驗選手。day1 100 60 0,t2調了整場沒調出來,害得t3也沒好好想。本來搭個對拍可能就調出t2了。用生命證明了自己的 能力在沒有除錯資料的情況下是負的。原以為day...

WC2019 全國模擬賽第一場 T1 題解

由於只會t1,沒法寫遊記,只好來寫題解了.給你乙個數列,每次可以任取兩個不相交的區間,取一次的貢獻是這兩個區間裡所有數的最小值,求所有取法的貢獻和,對 10 9 7 取模。數列長度 2 times 10 5 值域 1 10 9 預處理區間最小值,列舉選的兩個區間。include include in...

BJOI2019 刪數 線段樹

題目大意 乙個數列若能在有限次數內刪空,則稱這個數列可以刪空,一次刪除操作定義如下 記當前數列長度為 k 則刪掉數列中所有等於 k 的數。現在有乙個長度為 n 的數列 a 有 m 次修改操作,為單點變值 整體增加或者減少 1 問每次修改後,最少需要修改序列中多少個數,使得序列可以被刪除。資料範圍 n...