整数拆分 [dp+多项式插值]
题意
$1 \leq n \leq 10^{18}$
$2 \leq m \leq 10^{18}$
$1 \leq k \leq 20$
思路
n,m较小
首先考虑朴素的$k=1$问题:
$f[i]$表示分解$i$的方案数
那么转移方程如下
$f[i]=f[i-1]$,这里$i$不是$m$的倍数
$f[i]=f[i-1]+f[i/n]$,这里$i$是$m$的倍数
然后对于$k \neq 1$的情况就写个$ntt$就好了
但是这个只能解决$n,m \leq 1000$
另外一种dp
考虑另外一个和值域有关的方程:
一共有$1,m,m2,m3....$这些数
$f[i][j]$表示用了前$i$个数,得到和为$j$的方案数
注意这个状态表示是可以优化的
可以看到,如果已经用了前$i$个数,那么后面不管怎么用,从这种方案继续拓展可以得到的新的和与$j$在模$m^{i+1}$的意义下是同余的
也就是说,设$j=p \ast m^{i+1} + q$,那么从$f[i][j]$出去的状态的新的$j$写成这种方式,最后面的$q$都是相等的
因为我们最后要得到的是$n$,所以我们可以钦定这个$q = n % m^{i+1}$
这样,我们就可以换一个方式写方程:
$f[i][j]$表示用了前$i$个数,得到$j \ast m^{i+1} + n % m^{i+1}$的方案数
状态数还是太大,怎么办?
别急
我们打个表观察一下这个方程,其实可以发现一点:$f[i]j$是一批点值,它们在同一个$i$次多项式的图像上
别问我是怎么观察出来的,我也不知道
其实意会一下,就是你后面这个东西是呈$i+1$次增长的,所以每连续的$i$个就可以确定它的递推方式(其实这也是我瞎说的,我也不知道怎么证啊啊啊)
然后就很快乐了
我们每次只保存最前面的几个,然后往下一层推的时候,用插值把这一层的多项式插出来,然后定位到你推导下一层的前几个需要的那几个位置,再推导出下一层的前面几个
这样总效率是$\log^3n$的
那k呢?
我们这里可以利用一个类似多重背包的思想
显然,你把两个$k=1$的卷积起来,等价于你每一种数可以选两个了
所以$k$就代表每一种数可以选$k$个
于是就和上面的没啥差别了
总效率$O((k\log n)^3)$
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#define MOD 1000000007
#define ll long long
using namespace std;
inline ll read(){
ll re=0,flag=1;char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') flag=-1;
ch=getchar();
}
while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
ll qpow(ll a,ll b){
ll re=1;
while(b){
if(b&1) re=re*a%MOD;
a=a*a%MOD;b>>=1;
}
return re;
}
ll n,m,o;ll fac[2010],finv[2010];
void init(){
ll i,len=2000;
fac[0]=fac[1]=finv[0]=finv[1]=1;
for(i=2;i<=len;i++) fac[i]=fac[i-1]*i%MOD;
finv[len]=qpow(fac[len],MOD-2);
for(i=len;i>2;i--) finv[i-1]=finv[i]*i%MOD;
}
ll t1[2010],t2[2010],g[2010],f[2010],num[2010],cnt;
inline void add(ll &a,ll b){
a+=b;
if(a>=MOD) a-=MOD;
}
inline ll calc(ll k,ll lim){//这里我用了线性插出一个位置的方法
if(lim<=k) return g[lim];
ll i,tcnt;ll ans=0;
tcnt=0;
for(i=lim;i>=lim-k;i--){
if(tcnt==0) t1[tcnt]=1;
else t1[tcnt]=t1[tcnt-1]*((i+1)%MOD)%MOD;
tcnt++;
}
tcnt=k;
for(i=lim-k;i<=lim;i++){
if(tcnt==k) t2[tcnt]=1;
else t2[tcnt]=t2[tcnt+1]*((i-1)%MOD)%MOD;
tcnt--;
}
for(i=0;i<=k;i++){
tcnt=(((k-i)&1)?MOD-finv[k-i]:finv[k-i]);
add(ans,g[i]*t1[i]%MOD*t2[i]%MOD*finv[i]%MOD*tcnt%MOD);
}
assert(ans>=0&&ans<=MOD);
return ans;
}
int main(){
n=read();m=read();o=read();
ll i,j;
init();
num[++cnt]=1;
for(i=1;i<=n;i=i*m){
for(j=1;j<=o;j++)
num[++cnt]=i;
}
f[0]=1;f[1]=1;
for(i=2;i<=cnt;i++){
swap(f,g);
memset(f,0,sizeof(f));
if(num[i]==num[i-1]){//同一个数递推
for(j=0;j<=i;j++){
if(j) f[j]=f[j-1];
add(f[j],calc(i-1,j));
}
}
else{//不同的数递推
for(j=0;j<=i;j++){
if(j) f[j]=f[j-1];
add(f[j],calc(i-1,j*m+(n%num[i])/num[i-1]));
}
}
}
swap(f,g);
cout<<calc(cnt,n/num[cnt])<<'\n';
}
整数拆分 [dp+多项式插值]的更多相关文章
- 【NOI2019模拟2019.6.27】B (生成函数+整数划分dp|多项式exp)
Description: \(1<=n,k<=1e5,mod~1e9+7\) 题解: 考虑最经典的排列dp,每次插入第\(i\)大的数,那么可以增加的逆序对个数是\(0-i-1\). 不难 ...
- codeforces 955F Cowmpany Cowmpensation 树上DP+多项式插值
给一个树,每个点的权值为正整数,且不能超过自己的父节点,根节点的最高权值不超过D 问一共有多少种分配工资的方式? 题解: A immediate simple observation is that ...
- 整数拆分-dp问题
Integer Partition In number theory and combinatorics, a partition of a positive integer n, also call ...
- BZOJ 2173: 整数的lqp拆分( dp )
靠着暴力+直觉搞出递推式 f(n) = ∑F(i)f(n-i) (1≤i≤n) (直接想大概也不会很复杂吧...). f(0)=0 感受一下这个递推式...因为和斐波那契有关..我们算一下f(n)+f ...
- LightOJ 1336 Sigma Function(数论 整数拆分推论)
--->题意:给一个函数的定义,F(n)代表n的所有约数之和,并且给出了整数拆分公式以及F(n)的计算方法,对于一个给出的N让我们求1 - N之间有多少个数满足F(x)为偶数的情况,输出这个数. ...
- HDU1028 (整数拆分)
Ignatius and the Princess III Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K ...
- poj3181【完全背包+整数拆分】
题意: 给你一个数n,在给你一个数K,问你这个n用1-k的数去组合,有多少种组合方式. 思路: 背包重量就是n: 那么可以看出 1-k就是重物,价值是数值,重量是数值. 每个重物可以无限取,问题转化为 ...
- LeetCode 343.整数拆分 - JavaScript
题目描述:给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化. 返回你可以获得的最大乘积. 题目分析 题目中"n 至少可以拆分为两个正整数的和",这个条件说 ...
- Java实现 LeetCode 343 整数拆分(动态规划入门经典)
343. 整数拆分 给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化. 返回你可以获得的最大乘积. 示例 1: 输入: 2 输出: 1 解释: 2 = 1 + 1, 1 × ...
随机推荐
- 你可能不知道的 new.target
new 是构造函数生成实例的命令, ES6为 new 命令引入了 new.target属性.这个属性用于确定构造函数是怎么调用的. 在构造函数中, 如果一个构造函数不是通过 new操作符调用的, ne ...
- Java - 关于基础数据类型的形参和返回值
1. 当基础数据类型被当作形参时,最好使用其包装类,因为这样可方便调用者传参(基础数据类型亦或是其包装类都可) 2. 当基础数据类型被当作返回值时,最好使用原型,因为这样可以方便调用者接收返回值( ...
- 中通快递股份有限公司.net高级面试题
中通快递分布式技术开发 gc垃圾回收原理 .net中,托管代码的内存管理是自动的,由GC进行管理,而对于非托管代码,则需要.net手动处理 CLR运行时,内存分为:托管堆和栈,其中栈用于存储值类型 ...
- 关于移动端video标签层级问题
这是在微信中正常页面,就是用了一个原生video标签没做任何处理.然后顶部是固定页面顶端的,这个时候向上滑动页面时,会出现下图现象 这个时候正常人都会想到z-index问题,我也是这样想的,可惜很抱歉 ...
- MySQL查询优化 对not in 、in 的优化
因为 not in不走索引,所以不在不得已情况下,就不要使用not in 下面使用 join 来替代not in 做查询 select ID from A where ID not in (selec ...
- Qt的QWebChannel和JS、HTML通信/交互驱动百度地图
Qt的QWebChannel和JS.HTML通信/交互驱动百度地图 0 前言 我一个研究嵌入式的,不知道怎么就迷上了上位机,接了几个项目都是关于Qt,这个项目还是比较经典的,自己没事儿的时候也进行研究 ...
- P1198 [JSOI2008]最大数【树状数组】
题目描述 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值. 限制: L 不超过当前数列的长度. (L &g ...
- J.U.C 系列之Atomic原子类
一 什么是原子类? 所谓原子类必然是具有原子性的类,原子性操作--原子操作,百度百科中给的定义如下 "原子操作(atomic operation)是不需要synchronized" ...
- WPF制作的小时钟
原文:WPF制作的小时钟 周末无事, 看到WEB QQ上的小时钟挺可爱的, 于是寻思着用WPF模仿着做一个. 先看下WEB QQ的图: 打开VS, 开始动工. 建立好项目后, 面对一个空荡荡的页面, ...
- Android 判断屏幕方向一个大坑
正常的判断屏幕方向的代码: /** 获取屏幕是否是竖屏 * @return */ @SuppressLint("SwitchIntDef") public boolean isSc ...