CF1716D 解题报告
CF1716D 解题报告
题目大意:给 \(n,k\),问从 \(0\) 开始,第 \(i\) 步只能走 \((k+i-1)\) 的倍数,问分别走到 \(x\in[1,n]\) 的方案数。
题解
转换题意,有若干个 \((k+i)\) 大小的物品,取 \(y\) 时 \([k,y)\) 都要至少选过一次,问拿出总大小为 \(x\in[1,n]\) 的物品的方案数。
现场打的时候完全没想背包,于是寄了。容易发现这是个完全背包,但是前提是一定每个物品至少选一个。于是考虑滚动数组,分层后每次只由当前层和上一层贡献即可。初始状态第 \(0\) 层容量为 \(0\) 的方案数为 \(1\)。
考虑证明这个复杂度,容易发现对于每个容积 \(i\) ,其能选的物品 \(t\) 满足 \(tk+\sum_{j=1}^t(j-1)\le i\) ,那么 \(t\) 的上界是 \(O(\sqrt{n})\)。然后对于每个 \(i\in[1,n]\) 求出这个上界,选第 \(t\) 个物品时如果超过 \(i\) 的上界,那么 \(i\) 就不必枚举了(这个是个小的卡常技巧)。复杂度是 \(O(n\sqrt{n} )\) 未满。
关于就这个玩意为什么我考场上没想出来?我也不知道。
参考代码
#include<bits/stdc++.h>
#define ll long long
#define db double
#define file(a) freopen(#a".in","r",stdin),freopen(#a".out","w",stdout)
#define sky fflush(stdout)
#define gc getchar
#define pc putchar
namespace IO{
template<class T>
inline void read(T &s){
s=0;char ch=gc();bool f=0;
while(ch<'0'||'9'<ch) {if(ch=='-') f=1;ch=gc();}
while('0'<=ch&&ch<='9') {s=s*10+(ch^48);ch=gc();}
if(ch=='.'){
T p=0.1;ch=gc();
while('0'<=ch&&ch<='9') {s=s+p*(ch^48);p/=10;ch=gc();}
}
s=f?-s:s;
}
template<class T,class ...A>
inline void read(T &s,A &...a){
read(s);read(a...);
}
};
using IO::read;
const int N=2e5+3;
const int mods=998244353;
inline int inc(int x,int y){
return (x+=y)>=mods?x-mods:x;
}
inline int dec(int x,int y){
return (x-=y)<0?x+mods:x;
}
inline int mul(int x,int y){
return 1ll*x*y%mods;
}
int n,k;
int len[N];
int f[2][N],g[N];
int main(){
file(a);
read(n,k);
int t=0,sum=0;
for(int i=1;i<=n;++i){
while(i>=(t+1)*k+sum+t){
sum+=t;
++t;
}
len[i]=t;
}
int l=0;bool cur=0;
f[cur^1][0]=1;
for(int i=1;i<=len[n];++i,cur^=1){
while(len[l]<i-1) ++l;
int b=k+i-1;
memset(f[cur],0,sizeof(f[cur]) );
for(int j=l;j+b<=n;++j){
f[cur][j+b]=inc(f[cur][j+b],inc(f[cur][j],f[cur^1][j]) );
g[j+b]=inc(g[j+b],f[cur][j+b]);
}
}
for(int i=1;i<=n;++i){
printf("%d ",g[i]);
}
return 0;
}