AHOI2022山河重整 题解
首先容易得到 O(n2) 的解法,容易观察得出任意时刻范围都应是 [1,∑] 否则直接寄了。
考察 i 使得 [1,i] 都能凑出但 i+1 不行。则有 ∑ax⩽,令 f_i 表示其方案数。
可以考虑总方案减去不可行的方案,而不可行的方案可以依据更小的 f 求出。
考察这种最多只能加 \sqrt n 的背包,其实就是对每个 i\in [1,\sqrt n] 对 \sum [a_x\geqslant i] 做完全背包。
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
#define inf 1e9
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int n,mod,dp[maxn],ans,pw[maxn],f[maxn];
inline void add(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
inline void solve(int n){
if(n<=1)return;solve(n/2);
for(int i=0;i<=n;i++)f[i]=0;
int lim=sqrt(2*n);
for(int i=lim;i>=1;i--){
for(int j=n;j>i;j--)f[j]=f[j-i];
for(int j=i;j>=1;j--)f[j]=0;
for(int j=0;j+(j+2)*i<=n;j++)add(f[j+(j+2)*i],dp[j]);
for(int j=i;j<=n;j++)add(f[j],f[j-i]);
}for(int i=n/2+1;i<=n;i++)add(dp[i],mod-f[i]);
}
int main(){
n=read(),mod=read();
dp[0]=pw[0]=1;
for(int i=1;i<=n;i++)
pw[i]=(pw[i-1]+pw[i-1])%mod;
int lim=sqrt(2*n);
for(int i=lim;i>=1;i--){
for(int j=n;j>i;j--)dp[j]=dp[j-i];
for(int j=i;j>=1;j--)dp[j]=0;
for(int j=i;j<=n;j++)add(dp[j],dp[j-i]);
}solve(n);
for(int i=0;i<n;i++)
ans=(ans+1ll*dp[i]*pw[n-i-1])%mod;
ans=(pw[n]-ans+mod)%mod;
printf("%d\n",ans);
return 0;
}
深深地感到自己的弱小。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步