【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 ...
随机推荐
- [PLC]ST语言六:DI/EI/FEND/WDT/FOR/NEXT
一:DI/EI/FEND/WDT/FOR/NEXT 说明:简单的顺控指令不做其他说明. 控制要求:无 编程梯形图: 结构化编程ST语言:
- TensorFlow Python3.7环境下的源码编译(三)编译
这里要为仅支持 CPU 的 TensorFlow 构建一个 pip 软件包,需要调用以下命令: $ bazel build --cxxopt="-D_GLIBCXX_USE_CXX11_AB ...
- 记一次eslint规则配置
{ // 环境定义了预定义的全局变量. "env": { //环境定义了预定义的全局变量.更多在官网查看 "browser": true, "node ...
- Kafka发送到分区的message是否是负载均衡的?
首先说结论,是负载均衡的.也就是说,现在有一个producer,向一个主题下面的三个分区发送message,没有指定具体要发送给哪个partition, 这种情况,如果是负载均衡的,发送的消息应该均匀 ...
- 零基础学python之构建web应用(入门级)
构建一个web应用 前面的学习回顾: IDLE是Python内置的IDE,用来试验和执行Python代码,可以是单语句代码段,也可以是文本编辑器中的多语句程序. 四个内置数据结构:列表.字典.集合和元 ...
- 6.把建模工具导出的dea文件导入到three.js程序中
1.使用Three.js渲染导出的DAE 在Three.js中使用Collada(即.dae)文件的话,首先得要用到 ColladaLoader.js. 但是这个ColladaLoader.js并不包 ...
- mkfs命令详解
mkfs命令-->make filesystem的缩写:用来在特定的分区建立Linux文件系统 [命令作用] 该命令用来在特定的分区创建linux文件系统,常见的文件系统有ext2,ex ...
- 《找出1到正整数N中出现1的次数》
<找出1到正整数N中出现1的次数> 编程思想:依次求出正整数每个位数上出现1的次数,累加即可得到最后想要的结果:而每一位上出现1的个数与和它相邻的其它位数上的数字有关系(以此位置上的数为对 ...
- ubuntu16.04+cuda8.0+caffe
=========== 如果出现nvidia-smi failed to communicate with nvidia driver,循环登录情况,则: sudo apt-get remove -- ...
- java项目 相对路径(本项目的地址)
File file=new File(""); String abspath=file.getAbsolutePath(); System.out.println(abspath) ...