題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=5322
題目大意:對於1~n的全排列中的任意一個排列,對於在排列中的任意一個 i,如果存在一個離 i 最近的 j 滿足 i < j 且 A[i] < A[j],就在 i 和 j 之間建一條無向邊。照此規矩建完邊之後,如果聯通圖內的點數爲P,那麼這一個聯通圖的貢獻就是P*P,種排列的貢獻就爲所有聯通圖的貢獻的乘積。現在給你一個n,要你求出n的所有排列的貢獻之和。
題目思路:由於這個題目的組數特別多,所以我們考慮用dp預處理來解決這個問題。
dp[i] 表示 長度爲 i 的排列的貢獻之和。
我們考慮對於長度爲 i 的序列中的某一個序列當 最大的數 i 位於第 j 位時,前 j - 1 個數必然會與 i 形成聯通塊,後面的 i - j 個數就不會與前面的點聯通。這樣我們就可以推出如下的狀態轉移方程:
表示從除了最大值 i 以外的 i - 1 個數中選出 j - 1 一個放到 前 j - 1個位置裏,同時這個j - 1個數總共有 (j - 1) ! 種排列方式,這個聯通塊對答案的貢獻就爲 j^2,再乘上後面 i - j 個數自己形成聯通塊的貢獻之和就是最終答案了。
我們接着再用組合數的性質化簡一下這個式子
接着令,
式子就化成了。
這個式子就能用FFT來求卷積了(本題由於有模數,所以可以選擇用NTT來降低精度的誤差)。
但由於是要預處理出所有的dp的值,所以我們可以選擇用分治來降低複雜度,這樣總的複雜度就爲O(n*logn*logn+T)。
具體實現看代碼:
#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pb push_back
#define MP make_pair
#define lowbit(x) x&-x
#define clr(a) memset(a,0,sizeof(a))
#define _INF(a) memset(a,0x3f,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define debug(x) cout<<"["<<x<<"]"<<endl
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int>pii;
const int MX = 1e5+7;
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const int P = 998244353;
const int G = 3;
const int NUM = 20;
ll wn[NUM];
ll va[MX<<2],vb[MX<<2];
ll quick_mod(ll a, ll x, ll mod) {
ll ans = 1;
a %= mod;
while(x) {
if(x & 1)ans = ans * a % mod;
x >>= 1;
a = a * a % mod;
}
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 F[], int len) {
int j = len >> 1;
for(int i = 1; i < len - 1; i++) {
if(i < j) swap(F[i], F[j]);
int k = len >> 1;
while(j >= k)j -= k,k >>= 1;
if(j < k) j += k;
}
}
void NTT(ll F[], int len, int t) {
Rader(F, len);
int id = 0;
for(int h = 2; h <= len; h <<= 1) {
id++;
for(int j = 0; j < len; j += h) {
ll E = 1;
for(int k = j; k < j + h / 2; k++) {
ll u = F[k];
ll v = E * F[k + h / 2] % P;
F[k] = (u + v) % P;
F[k + h / 2] = (u - v + P) % P;
E = E * wn[id] % P;
}
}
}
if(t == -1) {
for(int i = 1; i < len / 2; i++)swap(F[i], F[len - i]);
ll inv = quick_mod(len, P - 2, P);
for(int i = 0; i < len; i++)F[i] = F[i] * inv % P;
}
}
void Conv(ll a[], ll b[], int len) {
NTT(a, len, 1);
NTT(b, len, 1);
for(int i = 0; i < len; i++)a[i] = a[i] * b[i] % P;
NTT(a, len, -1);
}
ll dp[MX],f[MX],invf[MX];
void init(){
f[0] = f[1] = 1;
for(int i = 2;i < MX;i++) f[i] = (f[i-1] * i) % P;
invf[MX-1] = quick_mod(f[MX-1],P-2,P);
for(int i = MX-2;i >= 0; i--) invf[i] = (invf[i+1] * (i+1)) % P;
}
void cdq(int l,int r){
if(l == r) return;
int m = (l + r) >> 1;
cdq(l,m);
int mx = (r - l + 1),len = 1;
while(len <= mx) len <<= 1;
for(int i = 0;i < len;i++){
if(l + i <= m) va[i] = dp[l+i]*invf[l+i] % P;
else va[i] = 0;
if(l + i < r) vb[i] = (ll)(i+1)*(i+1) % P;
else vb[i] = 0;
}
Conv(va,vb,len);
for(int i = m + 1;i <= r;i++)
dp[i] = (dp[i] + f[i-1]*(va[i-l-1]) % P) % P;
cdq(m+1,r);
}
void pre_solve(){
GetWn();
init();
dp[0] = 1;
cdq(0,100000);
}
int main(){
//FIN;
pre_solve();int n;
while(~scanf("%d",&n)) printf("%lld\n",dp[n]);
return 0;
}