有兩棵 \(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...