题目描述
n
≤
200
,
x
≤
1
0
9
n\le200,x\le 10^9
n≤200,x≤109
题目分析
容易想到枚举最大值的位置,然后递归两边,于是有这样的DP:
上面式子里面有个地方要改成
f
(
n
−
1
−
i
,
m
−
m
i
n
(
i
,
n
−
1
−
i
)
−
1
−
j
)
f(n-1-i,m-min(i,n-1-i)-1-j)
f(n−1−i,m−min(i,n−1−i)−1−j)
可以DP求出区间长度为 n n n 的 x x x 的最大值: m x [ n ] = m i n ( n + 1 2 , n + 1 − n + 1 2 ) mx[n]=min(\frac {n+1}2,n+1-\frac {n+1}2) mx[n]=min(2n+1,n+1−2n+1)
事实上已经可以通过DP的范围限制以及常数优化通过此题了,比如这么写:
//code by skyh, orz.
#include<bits/stdc++.h>
using namespace std;
int mod,n,x,T;
int fir[15],sec[15];
int dp[205][805],C[205][205],f[205];
const unsigned long long inf=1.4e19;
int main(){
freopen("s3mple.in","r",stdin);
freopen("s3mple.out","w",stdout);
scanf("%d",&mod); dp[0][0]=1; dp[1][1]=1;
for(int i=(f[1]=1,2);i<=200;++i) for(int j=1;j<=i;++j) f[i]=max(f[i],f[j-1]+f[i-j]+min(j,i-j+1));
for(int i=0;i<=200;++i) for(int j=C[i][0]=1;j<=i;++j) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
for(;scanf("%d%d",&fir[T+1],&sec[T+1])!=EOF;++T,n=max(n,fir[T]));
for(register int l=2;l<=n;++l)
for(register int i=1;i<=l;++i){
const int add=min(i,l-i+1);
for(register int j=l-1;j<=f[l]-add;++j){
register unsigned long long y=0;
const int lp=max(i-1,j-f[l-i]),rp=min(f[i-1],j-l+i);
register int k=lp;
for(k=lp;k+4<=rp;){
y+=1ll*dp[i-1][k]*dp[l-i][j-k];++k;
y+=1ll*dp[i-1][k]*dp[l-i][j-k];++k;
y+=1ll*dp[i-1][k]*dp[l-i][j-k];++k;
y+=1ll*dp[i-1][k]*dp[l-i][j-k];++k;
if(y>inf) y%=mod;
}
for(;k<=rp;++k){
y+=1ll*dp[i-1][k]*dp[l-i][j-k];
if(y>inf) y%=mod;
}
(dp[l][j+add]+=y%mod*C[l-1][i-1]%mod)%=mod;
}
}
for(int i=1;i<=T;++i) printf("%d\n",sec[i]>f[fir[i]]?0:dp[fir[i]][sec[i]]);
return 0;
}
还可以继续优化
转移相当于是在枚举
i
i
i 对
m
m
m 做卷积,于是可以将
f
(
n
,
m
)
f(n,m)
f(n,m) 看做多项式
F
n
(
x
)
=
∑
m
f
(
n
,
m
)
x
m
F_n(x)=\sum_{m}f(n,m)x^m
Fn(x)=∑mf(n,m)xm
次数就是权值,系数就是方案数。
F n ( x ) = ∑ i = 0 n − 1 F i ( x ) ∗ F n − i − 1 ( x ) ∗ ( n − 1 i ) ∗ x m i n ( i , n − 1 − i ) + 1 F_n(x)=\sum_{i=0}^{n-1}F_i(x)*F_{n-i-1}(x)*\binom {n-1}i*x^{min(i,n-1-i)+1} Fn(x)=i=0∑n−1Fi(x)∗Fn−i−1(x)∗(in−1)∗xmin(i,n−1−i)+1
F 0 ( x ) = 1 , F 1 ( x ) = x F_0(x)=1,F_1(x)=x F0(x)=1,F1(x)=x
如果模数是998244353,可以NTT, O ( n 3 log 2 n ) O(n^3\log^2 n) O(n3log2n)
因为
F
F
F 的最高次项不过
n
log
n
n\log n
nlogn,不会循环,可以把
F
F
F 转为点值表达式做卷积,那么转移复杂度
O
(
n
2
∗
n
log
n
)
O(n^2*n\log n)
O(n2∗nlogn)
询问
n
,
v
n,v
n,v,就拉格朗日插值还原
F
n
(
x
)
F_n(x)
Fn(x) 的系数表达式,答案就是
x
v
x^v
xv 项的系数。
插值可以预处理
G
i
(
x
)
=
∏
i
≠
j
x
−
j
G_i(x)=\prod_{i\neq j} x-j
Gi(x)=∏i=jx−j,那么每次求答案就是
O
(
n
log
n
)
O(n\log n)
O(nlogn) 的,预处理
O
(
(
n
log
n
)
2
)
O((n\log n)^2)
O((nlogn)2)
Code:
#include<bits/stdc++.h>
using namespace std;
const int M = 735, maxn = 205;
int n,X,mod,f[maxn][M+5],F[M+5],G[M+5][M+5],C[maxn][maxn],fac[M+5],inv[M+5],pw[M+5][maxn];
void Mul(int *f,int t){
for(int i=M+1;i;i--) f[i]=(f[i-1]-1ll*f[i]*t)%mod;
f[0]=1ll*f[0]*-t%mod;
}
void Div(int *f,int t){
int pre=f[M+1],now; f[M+1]=0;
for(int i=M;i>=0;i--,pre=now) now=f[i],f[i]=(pre+1ll*f[i+1]*t)%mod;
}
int main()
{
freopen("s3mple.in","r",stdin);
freopen("s3mple.out","w",stdout);
scanf("%d",&mod);
for(int i=0;i<maxn;i++)
for(int j=C[i][0]=1;j<=i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
for(int i=0;i<=M;i++) for(int j=pw[i][0]=1;j<maxn;j++) pw[i][j]=1ll*pw[i][j-1]*i%mod;
fac[0]=fac[1]=inv[0]=inv[1]=1;
for(int i=2;i<=M;i++) fac[i]=1ll*fac[i-1]*i%mod,inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=2;i<=M;i++) inv[i]=1ll*inv[i]*inv[i-1]%mod;
for(int i=0;i<=M;i++) f[0][i]=1,f[1][i]=i;
for(n=2;n<=200;n++)
for(int j=0;j<=M;j++)
for(int i=0;i<n;i++)
f[n][j]=(f[n][j]+1ll*f[i][j]*f[n-1-i][j]%mod*C[n-1][i]%mod*pw[j][min(i,n-1-i)+1])%mod;
//for(int i=0;i<=5;i++) cout<<f[3][i]<<' '; cout<<endl;
//cerr<<clock()<<endl;
F[0]=1;
for(int i=0;i<=M;i++) Mul(F,i);
for(int i=0;i<=M;i++) memcpy(G[i],F,sizeof F),Div(G[i],i);
while(~scanf("%d%d",&n,&X)){
if(X>M) {puts("0");continue;}
int ans=0;
for(int i=0;i<=M;i++){
int coef = 1ll*(M-i&1?-1:1)*f[n][i]*inv[i]%mod*inv[M-i]%mod;
ans=(ans+1ll*coef*G[i][X])%mod;
}
printf("%d\n",(ans+mod)%mod);
}
}
/*
int solve(int l,int r){
if(l>r) return 0;
int mid=(l+r)>>1;
return solve(l,mid-1)+solve(mid+1,r)+min(mid-l+1,r-mid+1);
}
*/