博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【bzoj4870】[Shoi2017]组合数问题 dp+快速幂/矩阵乘法
阅读量:5065 次
发布时间:2019-06-12

本文共 1288 字,大约阅读时间需要 4 分钟。

题目描述

输入

第一行有四个整数 n, p, k, r,所有整数含义见问题描述。
1 ≤ n ≤ 10^9, 0 ≤ r < k ≤ 50, 2 ≤ p ≤ 2^30 − 1

输出

一行一个整数代表答案。

样例输入

2 10007 2 0

样例输出

8


题目大意

问从nk个数中选出若干个,且选出数的数目mod k=r的方案数

题解

dp+快速幂/矩阵乘法

题目描述是骗人的,一个一个加根本不可能加的过来。

关于矩阵乘法的题解可以参考 ,时间复杂度为O(k^3logn),

我的做法是dp+快速幂。

(UPD:后来知道这其实就是循环矩阵乘法)

设 $f[i][j]$ 表示从 $i$ 个数中选出若干个,且选出的数的数目 $\text{mod} k=j$ 的方案数

那么有 $f[i1+i2][j]=\sum((j1+j2)mod k = j)f[i1][j1]∗f[i2][j2]$ 

这里可能比较难用数学语言表述,但事实上其中的求和符号仅是对于 $j1$ 和 $j2$ ,并不对于 $i1$ 和 $i2$ ,也就是说只要找到任意一组 $i1$ 和 $i2$ ,就可以用 $f[i1]$ 和 $f[i2]$ 推出。

发现这一点类似于乘方,于是我们可以使用快速幂的方法来快速推出f[nk]数组,时间复杂度为O(k^2logn).

注意一下这里k可能等于1,所以初始化时不能简单地将f[0][1]赋为1,而是将f[0][1%k]加上1。

#include 
#include
typedef long long ll;int p , k;struct data{ ll f[60]; data() { memset(f , 0 , sizeof(f)); } data operator*(const data a)const { int i , j; data tmp; for(i = 0 ; i < k ; i ++ ) for(j = 0 ; j < k ; j ++ ) tmp.f[(i + j) % k] = (tmp.f[(i + j) % k] + f[i] * a.f[j]) % p; return tmp; }}ans;data pow(data x , ll y){ data ans; ans.f[0] = 1; while(y) { if(y & 1) ans = ans * x; x = x * x , y >>= 1; } return ans;}int main(){ int n , r; scanf("%d%d%d%d" , &n , &p , &k , &r); ans.f[0] ++ , ans.f[1 % k] ++ ; printf("%lld\n" , pow(ans , (ll)n * k).f[r]); return 0;}

转载于:https://www.cnblogs.com/GXZlegend/p/6855497.html

你可能感兴趣的文章
HTML列表,表格与媒体元素
查看>>
雨林木风 GHOST_XP SP3 快速装机版YN12.08
查看>>
java对象的深浅克隆
查看>>
数据结构3——浅谈zkw线段树
查看>>
Introduction to my galaxy engine 2: Depth of field
查看>>
Python 3.X 练习集100题 05
查看>>
设计器 和后台代码的转换 快捷键
查看>>
Monkey测试结果分析
查看>>
STL——配接器、常用算法使用
查看>>
STL容器之vector
查看>>
无法向会话状态服务器发出会话状态请求
查看>>
数据中心虚拟化技术
查看>>
01入门
查看>>
复习文件操作
查看>>
SQL Server 使用作业设置定时任务之一(转载)
查看>>
第二阶段冲刺-01
查看>>
BZOJ1045 HAOI2008 糖果传递
查看>>
发送请求时params和data的区别
查看>>
JavaScript 克隆数组
查看>>
eggs
查看>>