【BZOJ 3326】[Scoi2013]数数 数位dp+矩阵乘法优化
挺好的数位dp……
先说一下我个人的做法:
经过观察,发现这题按照以往的思路从后往前递增,不怎么好推,然后我就大胆猜想,从前往后推,发现很好推啊,维护四个变量,从开始位置到现在有了i个数
f[i]:所有数的所有未包含最后一位的子串的和
s[i]:所有数的所有后缀子串的和
c[i]:所有数的所有后缀子串的个数
n[i]:所有数共有多少个
他们的转移依次是(k为进制数)
f[i]=f[i-1]*k+s[i-1]*k
s[i]=s[i-1]*k*k+c[i-1]*k*(k-1)/2+n[i-1]*k*(k-1)/2
c[i]=c[i-1]*k+n[i-1]*k
n[i]=n[i-1]*k
我们发现对于最高位低于上界的数,我们可以在确定最高位上是1~9之后用上面的转移一遍O(n)dp算出来.如果最高位等于上界的话,我们的转移不太一样,但是也只不过是把某些k改为了这一位的上届,而且如果本位未达到上届,往后转移还是老样子,然而每次都要从前往后走一遍,会T,不过,这很明显是个可以用矩阵乘法优化的dp,因为他的转移方式每次都一样,所以我们就可以加速了,然而这是4*4的矩阵再加上一个log,吃不消啊,但是我们可以预处理转移i(1<=i<=max(n,m))次的矩阵,这样就可以做到O(4^3*n)了,又因为这个矩阵是个上三角矩阵,所以我们加一些矩阵乘法时的优化就可以有有着一个10左右常数的O(n)的做法了,我们解决了这道题!!!
现在说一下别人的做法:
A掉之后,去网上看了看别人的题解,发现从后往前递增并不是不可以,而且根本就没有人从前往后推,更没有任何人的做法跟矩阵乘法有半点关系……
他们就是从后往前递增,推出来一个关于k的次幂的式子,通过预处理k的次幂,加上对于上界的处理来递推……
他们的做法基本上都是O(n)的,但是跑得和我差不多……
#include <cstdio>
#include <cstring>
#include <algorithm>
char xB[(<<)+],*xS,*xT;
#define gtc (xS==xT&&(xT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xT)?0:*xS++)
template <typename _t>
inline void read(_t &x){
register char ch=gtc;bool ud=false;
for(x=;ch<''||ch>'';ch=gtc)if(ch=='-')ud=true;
for(;ch>=''&&ch<='';x=(x<<)+(x<<)+ch-'',ch=gtc);
if(ud)x=-x;
}
typedef long long LL;
const int P=;
const int N=;
int a[][],b[],s[N][][],temp_a[][],temp_b[],c[],d[];
inline void get(int x[][],int y){
memset(temp_a,,sizeof(a));
register int i,j,k;
for(i=;i<;++i)
for(j=;j<;++j)
if(a[i][j])
for(k=;k<;++k)
if(x[j][k])
temp_a[i][k]=(temp_a[i][k]+(LL)x[j][k]*a[i][j])%P;
memcpy(s[y],temp_a,sizeof(s[y]));
}
inline void run(int x[][]){
memset(temp_b,,sizeof(temp_b));
register int i,j;
for(i=;i<;++i)
for(j=;j<;++j)
if(x[i][j])
temp_b[i]=(temp_b[i]+(LL)x[i][j]*d[j])%P;
memcpy(c,temp_b,sizeof(c));
}
int bit,digit[N],k,n,m,len;
inline int calc(){
int ans=,i;
d[]=,d[]=(LL)k*(k-)/%P,d[]=k-,d[]=k-;
for(i=;i<bit;++i)
run(s[i-]),ans=(ans+c[]+c[])%P;
memset(b,,sizeof(b)),b[]=;
for(i=bit;i>;--i){
d[]=((LL)b[]*(digit[i]-(i==bit))%P);
d[]=((LL)b[]*(digit[i]-(i==bit))+d[])%P;
d[]=((LL)k*b[]%P*(digit[i]-(i==bit))+(LL)b[]*((LL)digit[i]*(digit[i]-)/%P)+(LL)b[]*((LL)digit[i]*(digit[i]-)/%P))%P;
d[]=((LL)b[]*(digit[i]-(i==bit))+(LL)b[]*(digit[i]-(i==bit)))%P;
run(s[i-]);
ans=(ans+c[]+c[])%P;
b[]=(b[]+b[])%P;
b[]=((LL)k*b[]+(LL)(b[]+b[])*digit[i])%P;
++b[];
}
return (ans+b[]+b[])%P;
}
int main(){
read(k);int i,j,ans=;
a[][]=k,a[][]=k;
a[][]=(LL)k*k%P,a[][]=((LL)k*(k-)/)%P,a[][]=((LL)k*(k-)/)%P;
a[][]=k,a[][]=k;
a[][]=k;
s[][][]=s[][][]=s[][][]=s[][][]=;
for(read(n),i=n;i>;--i)read(digit[i]);
read(m),len=std::max(n,m);
for(i=;i<=len;++i)get(s[i-],i);
if(n==)ans=(ans-(LL)digit[]*(digit[]-)/%P+P)%P;
else{
for(--digit[],i=;i<=n;++i)
if(digit[i]<)digit[i]+=k,--digit[i+];
else break;
while(digit[n]==)--n;
bit=n,ans=(ans-calc()+P)%P;
}
for(i=m;i>;--i)read(digit[i]);
if(m==)ans=(ans+(LL)digit[]*(digit[]+)/%P)%P;
else bit=m,ans=(ans+calc())%P;
printf("%d\n",ans);
return ;
}
【BZOJ 3326】[Scoi2013]数数 数位dp+矩阵乘法优化的更多相关文章
- 【bzoj3329】Xorequ 数位dp+矩阵乘法
题目描述 输入 第一行一个正整数,表示数据组数据 ,接下来T行每行一个正整数N 输出 2*T行第2*i-1行表示第i个数据中问题一的解, 第2*i行表示第i个数据中问题二的解, 样例输入 1 1 样例 ...
- 洛谷2151[SDOI2009]HH去散步(dp+矩阵乘法优化)
一道良好的矩阵乘法优化\(dp\)的题. 首先,一个比较\(naive\)的想法. 我们定义\(dp[i][j]\)表示已经走了\(i\)步,当前在点\(j\)的方案数. 由于题目中限制了不能立即走之 ...
- bzoj4870: [Shoi2017]组合数问题(DP+矩阵乘法优化)
为了1A我居然写了个暴力对拍... 那个式子本质上是求nk个数里选j个数,且j%k==r的方案数. 所以把组合数的递推式写出来f[i][j]=f[i-1][j]+f[i-1][(j-1+k)%k].. ...
- BZOJ 3329: Xorequ [数位DP 矩阵乘法]
3329: Xorequ 题意:\(\le n \le 10^18\)和\(\le 2^n\)中满足\(x\oplus 3x = 2x\)的解的个数,第二问模1e9+7 \(x\oplus 2x = ...
- bzoj 3329: Xorequ【数位dp+矩阵乘法】
注意第一问不取模!!! 因为a+b=a|b+a&b,a^b=a|b-a&b,所以a+b=a^b+2(a&b) x^3x==2x可根据异或的性质以转成x^2x==3x,根据上面的 ...
- BZOJ.1875.[SDOI2009]HH去散步(DP 矩阵乘法)
题目链接 比较容易想到用f[i][j]表示走了i步后到达j点的方案数,但是题目要求不能走上一条走过的边 如果这样表示是不好转移的 可以考虑边,f[i][j]表示走了i步后到达第j条边的方案数,那么有 ...
- BZOJ_1662_[Usaco2006 Nov]Round Numbers 圆环数_数位DP
BZOJ_1662_[Usaco2006 Nov]Round Numbers 圆环数_数位DP Description 正如你所知,奶牛们没有手指以至于不能玩“石头剪刀布”来任意地决定例如谁先挤奶的顺 ...
- BZOJ_1026_[SCOI2009]windy数_数位DP
BZOJ_1026_[SCOI2009]windy数_数位DP 题意:windy定义了一种windy数.不含前导零且相邻两个数字之差至少为2的正整数被称为windy数. windy想知道, 在A和B之 ...
- [BZOJ 1009] [HNOI2008] GT考试 【AC自动机 + 矩阵乘法优化DP】
题目链接:BZOJ - 1009 题目分析 题目要求求出不包含给定字符串的长度为 n 的字符串的数量. 既然这样,应该就是 KMP + DP ,用 f[i][j] 表示长度为 i ,匹配到模式串第 j ...
随机推荐
- Linux shell 编写(2)
shell脚本中变量的定义和使用: 1.shell中变量名可以由字母,数字,下划线组成,但数字不能作为变量名的第一个字符. 2.通过赋值符号"="来定义一个变量 如:myname= ...
- [leetcode]从中序与后序/前序遍历序列构造二叉树
从中序与后序遍历序列构造二叉树 根据一棵树的中序遍历与后序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 例如,给出 中序遍历 inorder = [9,3,15,20,7] 后序遍历 po ...
- 关于java学习中的一些易错点(基础篇)
由JVM来负责Java程序在该系统中的运行,不同的操作系统需要安装不同的JVM,这样Java程序只需要跟JVM打交道,底层的操作由JVM去执行. JRE(Java Runtime Environmen ...
- Cocos2dx源码赏析(4)之Action动作
Cocos2dx源码赏析(4)之Action动作 本篇,依然是通过阅读源码的方式来简单赏析下Cocos2dx中Action动画的执行过程.当然,这里也只是通过这种方式来总结下对Cocos2dx引擎的理 ...
- Docker部署Golang
1. 安装docker 2. mkdir myDocker 3. cd myDocker && touch Dockerfile 4. Dockerfile写入 # 将golang ...
- 微软职位内部推荐-Software Engineer II_VS
微软近期Open的职位: Job Title: Software Engineer II Division: Visual Studio China – Developer Division Work ...
- django_models_Meta字段详解
Django模型类的Meta是一个内部类,它用于定义一些Django模型类的行为特性.而可用的选项大致包含以下几类 abstract 这个属性是定义当前的模型是不是一个抽象类.所谓抽象类是不会对应数据 ...
- centos7安装oracle亲测可用
http://www.linuxidc.com/Linux/2016-04/130559p2.htm
- 关于php的array_diff和array_diff_assoc的使用总结
关于php的array_diff和array_diff_assoc的使用总结 2015-11-07 17:01 184人阅读 评论(0) 收藏 举报 分类: php学习感想(1) 版权声明:本文为 ...
- Task 6.2冲刺会议四 /2015-5-17
今天主要是学习并熟悉了C#的开发流程,把他的文件的大体结构和每个组件之间的联系弄清楚之后.开始写服务器部分的内容.学习过程中,感觉网上的资料有些太鱼龙混杂了,不知道该怎么取舍.明天准备完善服务器的功能 ...