题意
https://vjudge.net/problem/QOJ-8834
对于一个正整数 \(X\),拆成若干 \(2^k\) 的和,满足不存在一种方案,对这些 \(2^k\) 分成两组,两组分别求和,high_bit(即 std::__lg
)相等。求对 \(X\) 的合法拆分方案数。对 X=1~n 分别求答案。(n<=1e6)
解
log2 相等就是尽量接近。物品大小是 2 的次幂,可以贪心:
对物品从大到小排序,维护变量 \(x\),初始为 \(0\),遍历物品,\(>x\) 则给 \(x\) 加,否则从 \(x\) 减。
设最后一段上升线段是 \(2^k\),则最小的两组之差是 \(d=-X\bmod 2^k\)。则分成两组为 \(\frac{X+d}2,\frac{X-d}2\)。
分析对 \(X\) 的合法拆分,枚举最后一段上升线段是 \(2^k\),显然 \(-X\bmod 2^k\ne0\),否则能分成两个等和组,不合法。
同时还需满足不存在第 k+1 位为 0 而 k+2 位及以上有 1 的情况。这也会必然导致不合法,原因见分成两组的表达式。
可以开始计算了。分成最后一段上升线段及以前、最后一段上升线段之后的下降段两部分计算。
记 \(f_i\) 表示 i 拆成若干 \(2^k\) 的和的方案数。不难完全背包。第二部分答案 \(f_{X\bmod 2^k}\)。
第一部分,每条线段都是 \(2^k\) 的倍数,可以整体除以 \(2^k\),变成对 \(2^{\operatorname{highbit}(X)-k+1}-1\) 的一种拆分问题,要求是“最后一段上升线段及以前”的形式。这要求这一部分的最后一条线段必须上升,可以通过枚举最后下降段的和来容斥。设 \(g_i\) 表示 \(2^i-1\) 的拆分问题答案,有 \(g_i=f_{2^i-1}-\sum_{j=1}^{i-1}g_jf_{2^{i-j}-1}\)。
两部分答案相乘就对了。
#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;
}