Atcoder Grand Contest 013 E - Placing Squares(组合意义转化+矩阵快速幂/代数推导,思维题)
这是一道难度 Cu 的 AGC E,碰到这种思维题我只能说:not for me,thx
然鹅似乎 ycx 把题看错了?
首先这个平方与乘法比较喜闻乐见,很容易与组合联系在一起,于是我们不妨把题目条件翻译成组合的语言:
- 有一排 \(n\) 个格子,你要在其中插入若干个隔板将其隔成若干段
- 有 \(m\) 个特殊格子 \(a_1,a_2,\dots,a_m\),\(\forall i\in [1,m]\) 你禁止在 \(a_i\) 与 \(a_{i}+1\) 之间放隔板
- 在相邻隔板之间的格子中需恰好放上一个黑球和一个白球(可以重合)
不难发现,上述题面中的”隔板“对应原题中的正方形边界,对于一段长度为 \(l\) 的段,在上面放一个黑球和一个白球的方案数为 \(l^2\),也就对应了原题面中的”平方“,而原题中的乘法在上述题面中被翻译成了乘法原理。因此我们就将这题与组合意义联系在了一起。
考虑设 \(dp_{i,j}\) 表示现在考虑了前 \(i\) 个位置,在当前位置到上一个隔板的区间中放上了 \(j\) 个球的方案数,其中 \(j\in [0,2]\)。
显然对于非特殊格子 \(i\) 我们有状态转移方程:
- \(dp_{i+1,0}=dp_{i,0}+dp_{i,2}\)(在 \(i\) 与 \(i+1\) 之间放隔板或不放隔板)
- \(dp_{i+1,1}=2dp_{i,0}+dp_{i,1}+2dp_{i,2}\)(如果放隔板,那么只能从 \(dp_{i,2}\) 转移过来,由于有黑白两种颜色的求所以乘个 \(2\);如果不放隔板,那么可以从 \(dp_{i,0},dp_{i,1}\) 转移过来,同理 \(dp_{i,0}\) 前的系数也需乘个 \(2\))
- \(dp_{i+1,2}=dp_{i,0}+dp_{i,1}+2dp_{i,2}\)(如果放隔板,那么只能从 \(dp_{i,2}\) 转移过来;如果不放隔板,那么可以从 \(dp_{i,0},dp_{i,1},dp_{i,2}\) 转移过来,以上四种情况均只有一种放法,而 \(dp_{i,2}\) 前系数的 \(2\) 是因为 \(dp_{i,2}\) 出现了两次)
对于特殊格子 \(i\) 我们同样可以得到类似的状态转移方程:
- \(dp_{i+1,0}=dp_{i,0}\)
- \(dp_{i+1,1}=2dp_{i,0}+dp_{i,1}\)
- \(dp_{i+1,2}=dp_{i,0}+dp_{i,1}+dp_{i,2}\)
那么这样转化有什么好处呢?在原题中我们直接 \(dp\) 不是太容易,或者说我们只能想出 1D1D 的 \(dp\) 状态。而在转化后的题面中则可以将转移写成常系数齐次递推的形式了。
由于上述状态转移方程以常系数齐次递推的形式出现,故我们可以把它写成矩阵的形式,即对于非特殊格子 \(i\),\(\begin{bmatrix}dp_{i+1,0}\\dp_{i+1,1}\\dp_{i+1,2}\end{bmatrix}=\begin{bmatrix}1&0&1\\2&1&2\\1&1&2\end{bmatrix}\begin{bmatrix}dp_{i,0}\\dp_{i,1}\\dp_{i,2}\end{bmatrix}\),对于特殊格子 \(i\),\(\begin{bmatrix}dp_{i+1,0}\\dp_{i+1,1}\\dp_{i+1,2}\end{bmatrix}=\begin{bmatrix}1&0&0\\2&1&0\\1&1&1\end{bmatrix}\begin{bmatrix}dp_{i,0}\\dp_{i,1}\\dp_{i,2}\end{bmatrix}\)。因此最后的 \(dp_n\) 可以写成一排 \(\begin{bmatrix}1&0&1\\2&1&2\\1&1&2\end{bmatrix}\) 和 \(\begin{bmatrix}1&0&0\\2&1&0\\1&1&1\end{bmatrix}\) 连乘的形式,其中 \(\begin{bmatrix}1&0&0\\2&1&0\\1&1&1\end{bmatrix}\) 的个数为 \(m\),显然矩乘算一下就好了,复杂度 \(m\log n\omega^3\),其中 \(\omega=3\)。
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MOD=1e9+7;
int n,m;
struct mat{
ll a[3][3];
mat(){memset(a,0,sizeof(a));}
mat operator *(const mat &rhs){
mat res;
for(int i=0;i<3;i++) for(int j=0;j<3;j++) for(int k=0;k<3;k++)
res.a[i][j]+=a[i][k]*rhs.a[k][j];
for(int i=0;i<3;i++) for(int j=0;j<3;j++) res.a[i][j]%=MOD;
return res;
}
};
int main(){
mat x,y,ret;ret.a[0][0]=1;
x.a[0][0]=1;x.a[0][1]=0;x.a[0][2]=1;
x.a[1][0]=2;x.a[1][1]=1;x.a[1][2]=2;
x.a[2][0]=1;x.a[2][1]=1;x.a[2][2]=2;
y.a[0][0]=1;y.a[0][1]=0;y.a[0][2]=0;
y.a[1][0]=2;y.a[1][1]=1;y.a[1][2]=0;
y.a[2][0]=1;y.a[2][1]=1;y.a[2][2]=1;
scanf("%d%d",&n,&m);int pre=-1;
for(int i=1,v;i<=m;i++){
scanf("%d",&v);int stp=v-pre-1;
mat z=x;for(;stp;stp>>=1,z=z*z) if(stp&1) ret=z*ret;
ret=y*ret;pre=v;
} int stp=n-pre-1;
mat z=x;for(;stp;stp>>=1,z=z*z) if(stp&1) ret=z*ret;
printf("%d\n",ret.a[2][0]);
return 0;
}
upd on 2021.3.9:
事实上还有一种基于朴素 \(dp\) 的代数优化方法。
首先我们不考虑组合意义,就直接设 \(dp_i\) 表示考虑前 \(i\) 个格子并在 \(i\) 与 \(i+1\) 放上隔板的答案。显然对于非特殊点 \(dp_i=\sum\limits_{j=0}^{i-1}dp_j(i-j)^2\),否则 \(dp_i=0\)。
考虑直接着手优化这个式子,我们考虑 \(dp_{i+1}\) 的递推式,(这里假设 \(i+1\) 不是标记点,即 \(dp_{i+1}\ne 0\))显然 \(dp_{i+1}=\sum\limits_{j=0}^idp_j(i+1-j)^2\),考虑把最后一项单独提出来,即 \(dp_{i+1}=\sum\limits_{j=0}^{i-1}dp_j(i+1-j)^2+dp_i\),把括号打开即有 \(dp_{i+1}=\sum\limits_{j=0}^{i-1}dp_j(i-j)^2+2\sum\limits_{j=0}^{i-1}dp_j(i-j)+\sum\limits_{j=0}^{i-1}dp_j+dp_i\)
我们记 \(a_i=\sum\limits_{j=0}^{i-1}dp_j,b_i=\sum\limits_{j=0}^{i-1}dp_j(i-j),c_i=\sum\limits_{j=0}^{i-1}dp_j(i-j)^2\),那么显然 \(dp_i=c_i\),还是考虑上面 \(dp_{i+1}\) 的展开式,那么有 \(dp_{i+1}=a_i+2b_i+c_i+dp_i\),假设我们知道了 \(a_{i},b_{i},c_{i}\),考虑怎样求出 \(a_{i+1},b_{i+1},c_{i+1}\),分 \(i\) 我特殊点和 \(i\) 不是特殊点讨论:
- 若 \(i\) 不是特殊点,那么
- \(a_{i+1}=\sum\limits_{j=0}^{i-1}dp_j+dp_{i}=a_i+c_i\)
- \(b_{i+1}=\sum\limits_{j=0}^{i-1}dp_j(i+1-j)+dp_{i}=\sum\limits_{j=0}^{i-1}dp_j(i-j)+\sum\limits_{j=0}^{i-1}dp_j+dp_i=b_i+a_i+c_i\)
- \(c_{i+1}=dp_{i+1}=a_i+2b_i+c_i+dp_i=a_i+2b_i+2c_i\)
- 若 \(i\) 是特殊点,那么
- \(a_{i+1}=\sum\limits_{j=0}^{i-1}dp_j=a_i\)
- \(b_{i+1}=\sum\limits_{j=0}^{i-1}dp_j(i+1-j)+dp_{i}=\sum\limits_{j=0}^{i-1}dp_j(i-j)+\sum\limits_{j=0}^{i-1}dp_j=b_i+a_i\)
- \(c_{i+1}=dp_{i+1}=a_i+2b_i+c_i\)
上述式子也可写成矩阵的形式,即对于关键点 \(i\),\(\begin{bmatrix}a_{i+1}\\b_{i+1}\\c_{i+1}\end{bmatrix}=\begin{bmatrix}1&0&1\\1&1&1\\1&2&2\end{bmatrix}\times\begin{bmatrix}a_{i}\\b_{i}\\c_{i}\end{bmatrix}\),否则 \(\begin{bmatrix}a_{i+1}\\b_{i+1}\\c_{i+1}\end{bmatrix}=\begin{bmatrix}1&0&0\\1&1&0\\1&2&1\end{bmatrix}\times\begin{bmatrix}a_{i}\\b_{i}\\c_{i}\end{bmatrix}\),这个同样可以矩阵 ksm 计算。时间复杂度同上。
代码与前一种解法大同小异:
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MOD=1e9+7;
int n,m;
struct mat{
ll a[3][3];
mat(){memset(a,0,sizeof(a));}
mat operator *(const mat &rhs){
mat res;
for(int i=0;i<3;i++) for(int j=0;j<3;j++) for(int k=0;k<3;k++)
res.a[i][j]+=a[i][k]*rhs.a[k][j];
for(int i=0;i<3;i++) for(int j=0;j<3;j++) res.a[i][j]%=MOD;
return res;
}
};
int main(){
mat x,y,ret;ret.a[0][0]=ret.a[1][0]=ret.a[2][0]=1;
x.a[0][0]=1;x.a[0][1]=0;x.a[0][2]=1;
x.a[1][0]=1;x.a[1][1]=1;x.a[1][2]=1;
x.a[2][0]=1;x.a[2][1]=2;x.a[2][2]=2;
y.a[0][0]=1;y.a[0][1]=0;y.a[0][2]=0;
y.a[1][0]=1;y.a[1][1]=1;y.a[1][2]=0;
y.a[2][0]=1;y.a[2][1]=2;y.a[2][2]=1;
scanf("%d%d",&n,&m);int pre=0;
for(int i=1,v;i<=m;i++){
scanf("%d",&v);int stp=v-pre-1;
mat z=x;for(;stp;stp>>=1,z=z*z) if(stp&1) ret=z*ret;
ret=y*ret;pre=v;
} int stp=n-pre-1;
mat z=x;for(;stp;stp>>=1,z=z*z) if(stp&1) ret=z*ret;
printf("%d\n",ret.a[2][0]);
return 0;
}
Atcoder Grand Contest 013 E - Placing Squares(组合意义转化+矩阵快速幂/代数推导,思维题)的更多相关文章
- Atcoder Grand Contest 001E - BBQ Hard(组合意义转化,思维题)
Atcoder 题面传送门 & 洛谷题面传送门 Yet another 思维题-- 注意到此题 \(n\) 数据范围很大,但是 \(a_i,b_i\) 数据范围很小,这能给我们什么启发呢? 观 ...
- AtCoder Grand Contest 013 E - Placing Squares
题目传送门:https://agc013.contest.atcoder.jp/tasks/agc013_e 题目大意: 给定一个长度为\(n\)的木板,木板上有\(m\)个标记点,距离木板左端点的距 ...
- AtCoder Grand Contest 013 C:Ants on a Circle
题目传送门:https://agc013.contest.atcoder.jp/tasks/agc013_c 题目翻译 给你一个周长为\(L\)的圆,有\(N\)只蚂蚁在圆上爬,速度为一单位距离每秒. ...
- AtCoder Grand Contest 013
这场打得蛮菜的,很晚才出BC,还一堆罚时…… A - Sorted Arrays 题目大意:将给定数列划分成单调不增或单调不减的区间,求最少区间数. 贪心即可. #include<cstdio& ...
- AtCoder Grand Contest 013 C :Ants on a Circle
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- AtCoder Grand Contest 013题解
传送门 \(A\) 先把相同的缩一起,然后贪心就可以了 //quming #include<bits/stdc++.h> #define R register #define fp(i,a ...
- 【agc013d】AtCoder Grand Contest 013 D - Piling Up
题意 盒子里有n块砖,每块的颜色可能为蓝色或红色. 执行m次三步操作: 1.从盒子里随便拿走一块砖 2.放入一块蓝砖和红砖到盒子里 3.从盒子里随便拿走一块砖 给定n,m 问拿出来的砖,可能有多少种不 ...
- AtCoder Grand Contest 012
AtCoder Grand Contest 012 A - AtCoder Group Contest 翻译 有\(3n\)个人,每一个人有一个强大值(看我的假翻译),每三个人可以分成一组,一组的强大 ...
- AtCoder Grand Contest 011
AtCoder Grand Contest 011 upd:这篇咕了好久,前面几题是三周以前写的... AtCoder Grand Contest 011 A - Airport Bus 翻译 有\( ...
随机推荐
- javascript-jquery对象的css处理
一.css基本属性处理 1.css()方法:获取css属性值.$("选择器").css(name);//获取匹配选择器的元素指定css属性值. 2.css()方法:设置css属性值 ...
- 【UE4 C++】学习笔记汇总
UE4 概念知识 基础概念--文件结构.类型.反射.编译.接口.垃圾回收.序列化[导图] GamePlay架构[导图] 类的继承层级关系[导图] 反射机制 垃圾回收机制/算法 序列化 Actor 的生 ...
- Sequence Model-week1编程题3-用LSTM网络生成爵士乐
Improvise a Jazz Solo with an LSTM Network 实现使用LSTM生成音乐的模型,你可以在结束时听你自己的音乐,接下来你将会学习到: 使用LSTM生成音乐 使用深度 ...
- vscode插件集合整理
针对PEPE8进行代码规范提示,安装flake8之后写代码的时候编辑器就会提示哪里出错,代码格式不规范也会提示,具体安装方式如下: 1.pip install flake8 2.安装flake8成功后 ...
- sql 多表联合查询更新
sqlserver: update A a set a.i = b.k from B b where a.key = b.key oracle : update A a set a.i = (sele ...
- [转]DDR3基础知识介绍
本文转自:(4条消息) xilinx ddr3 MIG ip核使用详解_admiraion123的博客-CSDN博客 1,DDR3基本内容介绍1.1,DDR3简介DDR3全称double-data-r ...
- 链表分割 牛客网 程序员面试金典 C++ Python
链表分割 牛客网 程序员面试金典 C++ Python 题目描述 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 给定一个链表的头指针 ListNode* p ...
- hdu 2830 Matrix Swapping II(额,,排序?)
题意: N*M的矩阵,每个格中不是0就是1. 可以任意交换某两列.最后得到一个新矩阵. 问可以得到的最大的子矩形面积是多少(这个子矩形必须全是1). 思路: 先统计,a[i][j]记录从第i行第j列格 ...
- 一步一步学ROP之linux_x64篇(蒸米spark)
目录 一步一步学ROP之linux_x64篇(蒸米spark) 0x00 序 0x01 Memory Leak & DynELF - 在不获取目标libc.so的情况下进行ROP攻击 0x02 ...
- Git 极速上手(超简单)
前言:本文主要介绍了一种快速入门使用Git的方法,通过四步完成本地仓库构建和推送到远程仓库(Github.Gitee码云),简单说明最常用的命令,不需要明白Git的原理即可使用,本文不介绍具体原理. ...