1.P6781 [Ynoi2008] rupq2.SNOI 2020 排列 题解3.ICPC WF 2022 2023 Bridging the Gap 过桥4.2023 ICPC Seoul Regional A. Apricot Seeds(Pjudge【NOIP Round #7】冒泡排序)5.CCPC Final 2023 B. Periodic Sequence
6.OCPC2024Day1/3rd ucup stage3 Formal Fring
7.[PKUSC 2023 D1T3] 天气预测8.[PKUWC 2025 D2T1]网友小 Z 的树9.[PKUWC2025 D2T2]盒子10.[集训队互测2024]建设终末树题意
https://vjudge.net/problem/QOJ-8834
对于一个正整数 ,拆成若干 的和,满足不存在一种方案,对这些 分成两组,两组分别求和,high_bit(即 std::__lg
)相等。求对 的合法拆分方案数。对 X=1~n 分别求答案。(n<=1e6)
解
log2 相等就是尽量接近。物品大小是 2 的次幂,可以贪心:
对物品从大到小排序,维护变量 ,初始为 ,遍历物品, 则给 加,否则从 减。
设最后一段上升线段是 ,则最小的两组之差是 。则分成两组为 。
分析对 的合法拆分,枚举最后一段上升线段是 ,显然 ,否则能分成两个等和组,不合法。
同时还需满足不存在第 k+1 位为 0 而 k+2 位及以上有 1 的情况。这也会必然导致不合法,原因见分成两组的表达式。
可以开始计算了。分成最后一段上升线段及以前、最后一段上升线段之后的下降段两部分计算。
记 表示 i 拆成若干 的和的方案数。不难完全背包。第二部分答案 。
第一部分,每条线段都是 的倍数,可以整体除以 ,变成对 的一种拆分问题,要求是“最后一段上升线段及以前”的形式。这要求这一部分的最后一条线段必须上升,可以通过枚举最后下降段的和来容斥。设 表示 的拆分问题答案,有 。
两部分答案相乘就对了。
#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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】