未来的未来の题解
这道题显然是DP我们可以考虑设
那么我们不难想出一个比较简单的转移。
我们可以考虑往后更新(因为比较好打)
为什么呢,其实很简单。
我们考虑放入一个新的数,前面
我们插入的这个数
这个数
所以一个
最少则
然后就能推出
所以就有了一个
#include<cstdio>
#include<iostream>
#define int long long
using namespace std;
const int N=1e5+10,K=1e3+10,mod=998244353;
int n,k,f[N][K],invjc=1;
int fpow(int x,int y){
int sum=1;
for(;y;y>>=1ll){
if(y&1ll)sum=(sum*x)%mod;
x=(x*x)%mod;
}
return sum;
}
int inv(int x){return fpow(x,mod-2);}
signed main(){
scanf("%lld%lld",&n,&k);
f[1][0]=1;
for(int i=2;i<=n;++i)invjc=(invjc*i)%mod;
invjc=inv(invjc);
for(int i=1;i<n;++i){
for(int j=0;j<k;++j){
for(int l=0;l<=i;++l){
f[i+1][(j+l)%k]=(f[i+1][(j+l)%k]+f[i][j])%mod;
}
}
}
for(int i=0;i<k;++i)printf("%lld ",(f[n][i]*invjc)%mod);
puts("");
return (0^0);
}
稳稳地T飞了
所以我们可以开始考虑优化。
空间
对于这种只由前一层更新过来的我们通常会使用滚动数组。
这样空间就不会炸了。(好像不开没炸???)
时间
我们尝试提高代码复杂度将DP的转移从从前往后更新改为从由前面转移。
会麻烦一些,我们先定义下限
那么我们会推出以下转移方程
当
因为如上文所述,每次插入的
最少则为
所以在这个下限
为什要考虑
因为 这个
那么
此时当
但是还有
综上所述,我们要两种讨论去转移。
然后就有了DP由前面转移的版本。
#include<cstdio>
#include<iostream>
#define int long long
using namespace std;
const int K=1e3+10,mod=998244353;
int n,k,f[2][K],invjc=1;
int fpow(int x,int y){
int sum=1;
for(;y;y>>=1ll){
if(y&1ll)sum=(sum*x)%mod;
x=(x*x)%mod;
}
return sum;
}
int inv(int x){return fpow(x,mod-2);}
signed main(){
scanf("%lld%lld",&n,&k);
f[1][0]=1;
for(int i=2;i<=n;++i)invjc=(invjc*i)%mod;
invjc=inv(invjc);
for(int i=2;i<=n;++i){
int u=(i&1),v=(u^1);
for(int j=0;j<k;++j){
int d=((j-i+1)%k+k)%k;
if(d<=j)for(int l=d;l<=j;++l)f[u][j]=(f[u][j]+f[v][l])%mod;
else{
for(int l=d;l<k;++l)f[u][j]=(f[u][j]+f[v][l])%mod;
for(int l=0;l<=j;++l)f[u][j]=(f[u][j]+f[v][l])%mod;
}
}
for(int j=0;j<k;++j)f[v][j]=0;
}
for(int i=0;i<k;++i)printf("%lld ",(f[(n&1)][i]*invjc)%mod);
puts("");
return (0^0);
}
居然有分!!!
其实时间复杂度还是
我们发现每次
所以我们搞一个前缀和优化,每次转移就是
但是由于取模常数很玄学ex。
这样带滚动数组的
所以我打表发现了一个规律。
当
取模常数啥的就基本没问题了!
然后就可以欢乐AC了!
AC code
#include<cstdio>
#include<iostream>
#define int long long
using namespace std;
const int K=1e3+10,mod=998244353;
int n,k,f[2][K],g[2][K],invjc=1;
int fpow(int x,int y){
int sum=1;
for(;y;y>>=1ll){
if(y&1ll)sum=(sum*x)%mod;
x=(x*x)%mod;
}
return sum;
}
int inv(int x){return fpow(x,mod-2);}
signed main(){
scanf("%lld%lld",&n,&k);
if(n>k){
n=inv(k);
for(;k--;)printf("%lld ",n);
puts("");
return (0^0);
}
f[1][0]=g[1][0]=1;
for(int i=1;i<k;++i)g[1][i]=1;
for(int i=2;i<=n;++i)invjc=(invjc*i)%mod;
invjc=inv(invjc);
for(int i=2;i<=n;++i){
int u=(i&1),v=(u^1);
for(int j=0;j<k;++j){
int d=((j-i+1)%k+k)%k;
if(d<=j){
if(d==0)f[u][j]=(g[v][j])%mod;
else f[u][j]=((g[v][j]-g[v][d-1])%mod+mod)%mod;
}
else{
f[u][j]=((g[v][k-1]-g[v][d-1])%mod+mod)%mod;
f[u][j]=(f[u][j]+g[v][j])%mod;
}
if(j==0)g[u][j]=f[u][j];
else g[u][j]=(g[u][j-1]+f[u][j])%mod;
}
}
for(int i=0;i<k;++i)printf("%lld ",(f[(n&1)][i]*invjc)%mod);
puts("");
return (0^0);
}
小小的提示:
注意负数取模和最后乘上全排列总数的逆元。
祝A (看到这了还不点赞吗QWQ)