题意

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;
}
posted on 2024-11-20 20:59  Zaunese  阅读(5)  评论(0编辑  收藏  举报