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;
}
posted @ 2022-08-05 14:59  cbdsopa  阅读(39)  评论(0编辑  收藏  举报