专栏文章
P8340 题解
P8340题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minyls7y
- 此快照首次捕获于
- 2025/12/02 10:28 3 个月前
- 此快照最后确认于
- 2025/12/02 10:28 3 个月前
不需要每次减半的做法。(?)
首先充要条件显然是 ,我们可以假定一定选 。
考虑容斥,选定一些 。
注意到 的和在 的时候就超过了 。因此我们会发现选定的 一定有 。
先考虑计算 且 的方案数。转化为每次将所有正数减一。考虑 表示目前考虑的 且 还不为 的方案数,容易转移。考虑到 ,我们可以 对于所有 求出答案。
接下来考虑带上选定我们该如何设计 dp 状态。我们希望在每次选定之前都有 dp 某一维等于真实的 。沿用上面的 即可正确转移:对于 , 就是实际上的 ,考虑选定的转移,枚举本次选定到下一次选定之间有多少 ,先“预付”每个这一段之间的 ,然后继续用同一个 dp 转移。也就是:。(当然由于是容斥系数显然应该是 。)
还有最后一个问题:最后一个选定的怎么办。我们发现在最后一个选定之后的部分我们可以直接忽略,也就是贡献是 。因此在每个 处将 计入答案即可。特判一下 以及啥都不选。总复杂度 。
由于他不让开 空间,我们得滚动数组。实现上讲,我们将 额外存下来,从 处反过来枚举 转移。
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 条评论,欢迎与作者交流。
正在加载评论...