题意

https://vjudge.net/problem/QOJ-8834

对于一个正整数 X,拆成若干 2k 的和,满足不存在一种方案,对这些 2k 分成两组,两组分别求和,high_bit(即 std::__lg)相等。求对 X 的合法拆分方案数。对 X=1~n 分别求答案。(n<=1e6)

log2 相等就是尽量接近。物品大小是 2 的次幂,可以贪心:

对物品从大到小排序,维护变量 x,初始为 0,遍历物品,>x 则给 x 加,否则从 x 减。

设最后一段上升线段是 2k,则最小的两组之差是 d=Xmod2k。则分成两组为 X+d2,Xd2

分析对 X 的合法拆分,枚举最后一段上升线段是 2k,显然 Xmod2k0,否则能分成两个等和组,不合法。

同时还需满足不存在第 k+1 位为 0 而 k+2 位及以上有 1 的情况。这也会必然导致不合法,原因见分成两组的表达式。

可以开始计算了。分成最后一段上升线段及以前、最后一段上升线段之后的下降段两部分计算。

fi 表示 i 拆成若干 2k 的和的方案数。不难完全背包。第二部分答案 fXmod2k

第一部分,每条线段都是 2k 的倍数,可以整体除以 2k,变成对 2highbit(X)k+11 的一种拆分问题,要求是“最后一段上升线段及以前”的形式。这要求这一部分的最后一条线段必须上升,可以通过枚举最后下降段的和来容斥。设 gi 表示 2i1 的拆分问题答案,有 gi=f2i1j=1i1gjf2ij1

两部分答案相乘就对了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define fi first
#define se second
#define mkp std::make_pair
using llu=long long unsigned;
using ll=long long;
using std::max;
using std::min;
template<class T> void cmax(T&a,T b){a=max(a,b);}
template<class T> void cmin(T&a,T b){a=min(a,b);}
const ll mod=998244353;
const int NV=1e6;
namespace xm{
int f[NV+5],g[NV+5];
void _(){
int N;
scanf("%d",&N);
f[0]=1;
for(int k=1;k<=N;k<<=1)
for(int i=k;i<=N;++i) f[i]=(f[i]+f[i-k])%mod;
for(int i=1;i<20;++i){
g[i]=f[(1<<i)-1];
for(int j=1;j<i;++j) g[i]=(g[i]-(ll)g[j]*f[(1<<i-j)-1])%mod;
}
for(int i=1;i<=N;++i) {
ll ans=0;
int k=std::__lg(i);
for(int j=k;~j;--j)
if(i>>j&1){
ans=(ans+(ll)g[k-j+1]*f[i&(1<<j)-1])%mod;
}else break;
printf("%lld ",(ans+mod)%mod);
}
puts("");
}
}
int main(){
xm::_();
return 0;
}
posted on   Zaunese  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】



点击右上角即可分享
微信分享提示