专栏文章

P8340 题解

P8340题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@minyls7y
此快照首次捕获于
2025/12/02 10:28
3 个月前
此快照最后确认于
2025/12/02 10:28
3 个月前
查看原文
不需要每次减半的做法。(?)
首先充要条件显然是 1+i=1jaiaj+11+\sum_{i=1}^ja_i\geq a_{j+1},我们可以假定一定选 n+1n+1
考虑容斥,选定一些 1+i=1jai<aj+11+\sum_{i=1}^ja_i<a_{j+1}
注意到 a1<a2<<aka_1<a_2<\dots<a_k 的和在 k=O(n)k=O(\sqrt n) 的时候就超过了 nn。因此我们会发现选定的 jj 一定有 jO(n)j\leq O(\sqrt n)
先考虑计算 a1<a2<<aka_1<a_2<\dots<a_ksum=nnsum=n'\leq n 的方案数。转化为每次将所有正数减一。考虑 dpi,jdp_{i,j} 表示目前考虑的 sum=isum=iakj+1aka_{k-j+1}\sim a_k 还不为 00 的方案数,容易转移。考虑到 kO(n)k\leq O(\sqrt{n'}),我们可以 O(nn)O(n\sqrt n) 对于所有 (k,n)(k,n') 求出答案。
接下来考虑带上选定我们该如何设计 dp 状态。我们希望在每次选定之前都有 dp 某一维等于真实的 sumsum。沿用上面的 dpi,jdp_{i,j} 即可正确转移:对于 dpi,j=1dp_{i,j=1}ii 就是实际上的 sumsum,考虑选定的转移,枚举本次选定到下一次选定之间有多少 axa_x,先“预付”每个这一段之间的 axia_x\gets i,然后继续用同一个 dp 转移。也就是:dpi,1dpi+(i+2)×k,kdp_{i,1}\to dp_{i+(i+2)\times k,k}。(当然由于是容斥系数显然应该是 1-1。)
还有最后一个问题:最后一个选定的怎么办。我们发现在最后一个选定之后的部分我们可以直接忽略,也就是贡献是 2nsum12^{n-sum-1}。因此在每个 dpi,1dp_{i,1} 处将 2ni1dpi,1-2^{n-i-1}dp_{i,1} 计入答案即可。特判一下 i=0i=0 以及啥都不选。总复杂度 O(nn)O(n\sqrt n)
由于他不让开 O(nn)O(n\sqrt n) 空间,我们得滚动数组。实现上讲,我们将 dpi,1dp_{i,1} 额外存下来,从 i+(i+2)×ki+(i+2)\times k 处反过来枚举 kk 转移。
CPP
#include <bits/stdc++.h>
#define mid ((l+r)>>1)
#define lowbit(i) (i&(-i))
using namespace std;
int mod;
inline void add(int &i,int j){
	i+=j;
	if(i>=mod) i-=mod;
}
inline int addv(int i,int j){
	i+=j;
	if(i>=mod) i-=mod;
	return i;
}
const int B=1000;
int dp[1005][1005];
int del[1005];
int f[1000005];
int pw2[1000005];
signed main(){
//	freopen("test.in","r",stdin);
//	freopen("test.out","w",stdout);
//	ios::sync_with_stdio(false);
//	cin.tie(0),cout.tie(0);
	int n; cin>>n>>mod;
	pw2[0]=1; for(int i=1;i<=1000000;i++) pw2[i]=addv(pw2[i-1],pw2[i-1]);
	for(int i=1;i<=B;i+=2) dp[0][i]=1;
	f[0]=1;
	int ans=pw2[n-1];
	for(int i=1;i<n;i++){
		int p=i%B;
		memset(dp[p],0,sizeof(dp[p]));
		del[0]=p;
		for(int j=1;j<=B;j++){
			int x=i-j*2;
			if(x%(j+1)==0) add(dp[p][j],mod-f[x/(j+1)]);
		}
		for(int j=1;j<=B;j++) del[j]=(del[j-1]==0)?999:del[j-1]-1;
		for(int j=1;j<=B;j++) add(dp[p][j],addv(dp[del[j]][j],dp[del[j]][j+1]));
		f[i]=dp[p][1];//jump
		add(ans,mod-1ll*dp[p][1]*pw2[n-i-1]%mod);
	}
	cout<<ans;
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...