JZOJ 4390. 【GDOI2016模拟3.16】图计数

本文探讨了一个整数拆分的问题,通过使用背包算法来寻找所有可能的拆分数目,并利用五边形数理论进行优化。介绍了如何将问题划分为两部分分别使用无限背包和有限背包解决的方法。

题目

一个整数n,设能够被拆成一些整数的和的方案数为ans。
mansmans。对一个质数p取模。
这些整数没有顺序。
比如:
n,m200000n,m≤200000

解题思路

背包。
求一条分割线,将1~n的数划分为A组和B组,A组的用无限背包解决,B组的用有限背包解决。
那么按照nn来分割。
最基本的转移:A组的按照无限背包来做,即做到数i,目前装了j的容量。
g[i][j]=g[i1][j]+g[i][ji]g[i][j]=g[i−1][j]+g[i][j−i]
听说还有五边形数。
B组的按照有限背包做,即序列中有i个数,目前装了j的容量。
最基本的转移:要么加n+1n+1,要么序列中所有的数+1+1
f[i][j]=f[i1][jn1]+f[i][ji]f[i][j]=f[i−1][j−n−1]+f[i][j−i]
最后答案卷一下就行了。
陷阱:欧拉定理。求ans的时候模的是p-1而不是p。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 200010
#define mo 999999598
#define LL long long
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int f[2][N],g[2][N],sum[N];
int o,n,i,j,k,l;
LL ans,m;
LL ksm(LL x,LL y,LL Mo){
    LL rs=1;
    for(;y;y>>=1,x=(x*x)%Mo)if(y&1)rs=(rs*x)%Mo;
    return rs;
}
int main(){
    scanf("%d%d",&n,&m);
    l=sqrt(n);
    f[0][0]=1;sum[0]=1;
    fo(i,1,l){
        o=1-o;
        memset(f[o],0,sizeof(f[o]));
        fo(j,l+1,n){
            f[o][j]=(f[1-o][j-l-1]+f[o][j-i])%mo;
            sum[j]=(sum[j]+f[o][j])%mo;
        }
    }
    g[0][0]=1;
    o=0;
    fo(i,1,l){
        o=1-o;
        memset(g[o],0,sizeof(g[o]));
        fo(j,0,n){
            g[o][j]=g[1-o][j];
            if(j>=i)g[o][j]=(g[o][j]+g[o][j-i])%mo;
        }
    }
    fo(i,0,n)ans=(ans+((LL)g[o][i]*sum[n-i])%mo)%mo;
    ans=ksm(m,ans,mo+1);
    printf("%lld",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值