1 条题解

  • 0
    @ 2026-7-4 23:16:11

    #include <cstdio>
    const int M = 500005;
    const int B = 1000;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,ans,f[M],g[M],b[M];
    void add(int &x,int y) {if((x+=y)>=m) x-=m;}
    void solve(int n)
    {
    	if(n<=1) return ;solve(n>>1);//递归左边
    	for(int i=0;i<=n;i++) g[i]=0;
    	for(int i=B;i;i--)//去重的过程仍然是拆分数
    	{
    		for(int j=n;j>=i;j--)
    			g[j]=g[j-i];
    		for(int j=0;j+i*(j+2)<=n;j++)
    			add(g[j+i*(j+2)],f[j]);
    		//这里的初始化变成了添加 i 个 j+2 的数字
    		//因为要计算 [j+2,i] 内选数和为 j-i 的方案数 
    		for(int j=i;j<=n;j++)
    			add(g[j],g[j-i]);
    	}
    	for(int i=(n>>1)+1;i<=n;i++)
    		add(f[i],m-g[i]);//正难则反
    }
    int main()
    {
    	n=read();m=read();
    	for(int i=B;i;i--)
    	//此时还在增加的有 i 个数,转移就是整体加 1
    	{
    		for(int j=n;j>=i;j--) f[j]=f[j-i];f[i]=1;
    		//初始化,现在的 i 个数每个值都是 1
    		for(int j=i;j<=n;j++) add(f[j],f[j-i]);
    		//可以整体增加多次,所以做完全背包
    	}
    	f[0]=b[0]=1;solve(n);
    	for(int i=1;i<=n;i++) b[i]=b[i-1]*2%m;
    	for(int i=0;i<n;i++)
    		add(ans,1ll*f[i]*b[n-i-1]%m);
    	printf("%d\n",(b[n]+m-ans)%m);
    }
    
    
    • 1

    信息

    ID
    7246
    时间
    3000ms
    内存
    1024MiB
    难度
    10
    标签
    递交数
    2
    已通过
    1
    上传者