日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

HDU5322 - cdq分治FFT加速dp

發(fā)布時(shí)間:2023/12/3 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HDU5322 - cdq分治FFT加速dp 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

5322 Hope [CDQ分治FFT加速計(jì)算dp]


題意

每一個(gè)每一個(gè)排列,排列中每個(gè)數(shù)向它后面第一個(gè)比它大的數(shù)連一條邊.

每個(gè)排列對(duì)于答案的貢獻(xiàn)是這個(gè)排列所生成的圖中的每一個(gè)聯(lián)通量中點(diǎn)的個(gè)數(shù)的平方之積.

例如:排列

1,2,3,6,4,51,2,3,6,4,51,2,3,6,4,5

其中

1,2,3,61,2,3,61,2,3,6形成一個(gè)大小為444的聯(lián)通分量.

4,54,54,5形成一個(gè)大小為222的聯(lián)通分量.

那么這個(gè)排列的貢獻(xiàn)就是42?22=644^2*2^2 = 6442?22=64.

我們需要求所有排列的貢獻(xiàn).

題解

這種題做法一般就是dpdpdp.

我們枚舉排列中最大的數(shù)nnn的所在的位置,當(dāng)nnn的位置固定以后,所有在nnn前面的數(shù)都會(huì)與nnn形成一個(gè)連通分量.而后面的所有的數(shù)就組成了一個(gè)子問(wèn)題.

定義dp[n]dp[n]dp[n]表示nnn的所有排列所形成的貢獻(xiàn)之和.

我們可以得到遞推公式.

dp[n]=∑i=1nCn?1i?1?(i?1)!?i2?dp[n?i]dp[n] = \sum_{i=1}^{n}C_{n-1}^{i-1}*(i-1)!*i^2*dp[n-i]dp[n]=i=1n?Cn?1i?1??(i?1)!?i2?dp[n?i]

化簡(jiǎn)得到

dp[n]=(n?1)!∑i=1ni2?dp[n?i](n?i)!dp[n] = (n-1)!\sum_{i=1}^{n}i^2*\frac{dp[n-i]}{(n-i)!}dp[n]=(n?1)!i=1n?i2?(n?i)!dp[n?i]?

F[i]=dp[i]i!,G[i]=i2F[i]=\frac{dp[i]}{i!},G[i]=i^2F[i]=i!dp[i]?,G[i]=i2

那么

dp[n]=(n?1)!∑i=1nG[i]F[n?i]dp[n] = (n-1)!\sum_{i=1}^{n}G[i]F[n-i]dp[n]=(n?1)!i=1n?G[i]F[n?i]

觀察到dp[n]dp[n]dp[n]的遞推公式是一個(gè)卷積的形式.

求卷積我們顯然想到了FFT/NTTFFT/NTTFFT/NTT來(lái)加速dpdpdp的計(jì)算,對(duì)于要求出所有的dp[n]dp[n]dp[n],因此我們需要用CDQCDQCDQ分治來(lái)輔助計(jì)算FFT/NTTFFT/NTTFFT/NTT.

采用分治策略,即將區(qū)間F[l,r]F[l,r]F[l,r]分成區(qū)間F[l,mid]F[l,mid]F[l,mid]F[mid+1,r]F[mid+1,r]F[mid+1,r]兩個(gè)區(qū)間.

F[l,mid]F[l,mid]F[l,mid]區(qū)間的內(nèi)容和GGG多項(xiàng)式做卷積,即可計(jì)算出F[l,mid]F[l,mid]F[l,mid]部分對(duì)于F[mid+1,r]F[mid+1,r]F[mid+1,r]的影響.注意!這里要求F[l,mid]F[l,mid]F[l,mid]必須已經(jīng)完整的被計(jì)算出來(lái)了.隨后再地歸計(jì)算F[mid+1,r]F[mid+1,r]F[mid+1,r]部分,解決它們內(nèi)部的貢獻(xiàn)以來(lái).

因此代碼的大致框架就是:

void solve(int l,int r) {if(l == r) return ;int mid = (l + r) >> 1;solve(l,mid);//保證區(qū)間F[l,mid]已經(jīng)被完整的計(jì)算出來(lái)了Conv();//求卷積,計(jì)算F[l,mid]對(duì)F[mid+1,r]的貢獻(xiàn).注意此時(shí)F[1,l-1]對(duì)F[mid+1,r]的貢獻(xiàn)早已被求過(guò)了.//注意在此時(shí),F[mid+1,r]內(nèi)部依賴(lài)于F[1,mid]的貢獻(xiàn)已經(jīng)全部被計(jì)算出來(lái)了,因此下一步求內(nèi)部F[mid+1,r]對(duì)F[mid+1,r]的貢獻(xiàn).solve(mid+1,r); }

舉個(gè)栗子

當(dāng)n=4n=4n=4時(shí)刻
初始[1,1][1,1][1,1]就算完整的被計(jì)算出來(lái)了.
先求[1,1][1,1][1,1]對(duì)[2,2][2,2][2,2]的貢獻(xiàn),從而得到了完整的[1,2][1,2][1,2].
然后計(jì)算[1,2][1,2][1,2]對(duì)[3,4][3,4][3,4]的貢獻(xiàn),此時(shí)[3,3][3,3][3,3]已經(jīng)被完整的計(jì)算出來(lái)了.
然后算[3,3][3,3][3,3]對(duì)于[4,4][4,4][4,4]的貢獻(xiàn),導(dǎo)致[4,4][4,4][4,4]被完整的計(jì)算出來(lái).
至此[1,4][1,4][1,4]都被完整的計(jì)算出來(lái)了.

注意

其中計(jì)算[l,mid][l,mid][l,mid]對(duì)[mid+1,r][mid+1,r][mid+1,r]的貢獻(xiàn)的時(shí)候,需要注意:

x∈[mid+1,r]x \in [mid+1,r]x[mid+1,r]

F[x]←F[l+0]?G[x?l]+...+F[l+(mid?l)]?G[x?mid]F[x] \leftarrow F[l+0]*G[x-l] + ... + F[l+(mid-l)]*G[x-mid]F[x]F[l+0]?G[x?l]+...+F[l+(mid?l)]?G[x?mid]

F[l],F[l+1],...,F[mid]F[l],F[l+1],...,F[mid]F[l],F[l+1],...,F[mid]填在數(shù)組a[0],a[1],...,a[mid?l]a[0],a[1],...,a[mid-l]a[0],a[1],...,a[mid?l]的位置.

G[0],G[1],G[2],...,G[n]G[0],G[1],G[2],...,G[n]G[0],G[1],G[2],...,G[n]填在數(shù)組b[0],b[1],b[2],..,b[n]b[0],b[1],b[2],..,b[n]b[0],b[1],b[2],..,b[n]的位置.
然后求個(gè)卷積c=a?bc = a\bigotimes bc=a?b

最后卷積數(shù)組的c[x?l]c[x-l]c[x?l]位置的值就是F[x]F[x]F[x].

描述上稍微有點(diǎn)問(wèn)題,FFFdpdpdp的關(guān)系有點(diǎn)混淆,諸位看我代碼就ok了.

代碼

#include <iostream> #include <algorithm> #include <cstring> #define pr(x) std::cout << #x << ':' << x << std::endl #define rep(i,a,b) for(int i = a;i <= b;++i) #define clr(x) memset(x,0,sizeof(x)) #define setinf(x) memset(x,0x3f,sizeof(x)) #define Max(x,y) x = std::max(x,y) #define Min(x,y) x = std::min(x,y) #define Add(x,y) x = (((x)+(y))%P) #define Sub(x,y) x = (((x)-(y)+P%P) #define Mul(x,y) x = ((x)*(y)%P) typedef long long LL; const int N = 1 << 20; const int P = 998244353; const int G = 3; const int NUM = 20;LL wn[NUM]; LL a[N], b[N];LL quick_mod(LL a, LL b, LL m) {LL ans = 1;a %= m;while(b){if(b & 1){ans = ans * a % m;b--;}b >>= 1;a = a * a % m;}return ans; }void GetWn() {for(int i = 0; i < NUM; i++){int t = 1 << i;wn[i] = quick_mod(G, (P - 1) / t, P);} } void Rader(LL a[], int len) {int j = len >> 1;for(int i = 1; i < len - 1; i++){if(i < j) std::swap(a[i], a[j]);int k = len >> 1;while(j >= k){j -= k;k >>= 1;}if(j < k) j += k;} }void NTT(LL a[], int len, int on) {Rader(a, len);int id = 0;for(int h = 2; h <= len; h <<= 1){id++;for(int j = 0; j < len; j += h){LL w = 1;for(int k = j; k < j + h / 2; k++){LL u = a[k] % P;LL t = w * a[k + h / 2] % P;a[k] = (u + t) % P;a[k + h / 2] = (u - t + P) % P;w = w * wn[id] % P;}}}if(on == -1){for(int i = 1; i < len / 2; i++)std::swap(a[i], a[len - i]);LL inv = quick_mod(len, P - 2, P);for(int i = 0; i < len; i++)a[i] = a[i] * inv % P;} }void Conv(LL a[], LL b[], int n) {NTT(a, n, 1);NTT(b, n, 1);for(int i = 0; i < n; i++)a[i] = a[i] * b[i] % P;NTT(a, n, -1); }LL dp[N],Fac[N],iFac[N];void solve(int l,int r) {if(l == r) return ;int mid = (l + r) >> 1;solve(l,mid);int len = 1;while(len <= (r-l+1)) len <<= 1;rep(i,0,len) {b[i] = 1LL*i*i%P;}rep(i,l,mid) {a[i-l] = dp[i]*iFac[i]%P;}rep(i,mid-l+1,len) a[i] = 0;Conv(a,b,len);rep(i,mid+1,r) {dp[i] = (dp[i] + (a[i-l]*Fac[i-1]%P))%P;}solve(mid+1,r); }int main() {Fac[0] = iFac[0] = 1;rep(i,1,N-1) {Fac[i] = Fac[i-1]*i % P;iFac[i] = quick_mod(Fac[i],P-2,P);}GetWn();dp[0] = 1;solve(0,100000);int n;while(std::cin >> n) {std::cout << dp[n] << std::endl;}return 0; }

總結(jié)

以上是生活随笔為你收集整理的HDU5322 - cdq分治FFT加速dp的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。