Portal

Yet another 1e9+7

Yet another 计数 dp

Yet another 我做不出来的题

考虑合法的按键方式长啥样。假设我们依次按下了 \(p_1,p_2,\dots,p_m\) 号按键。

若 \(m=1\),则序列 \(p_1,p_2,\dots,p_m\) 显然合法。

若 \(m>1\),则 \(p_1,p_2,\dots,p_m\) 必须有唯一最大值 \(x\)(否则的话第二次按 \(x\) 的时候就不合法了)。假设 \(x\) 将原序列分成两个子序列 \(p_1,p_2,\dots,p_{k-1}\) 和 \(p_{k+1},p_{k+2},\dots,p_{m}\),那么这两个序列的最大值必须都小于 \(x\),否则原序列就不会有唯一最大值 \(x\) 了。并且 \(p_1,p_2,\dots,p_{k-1}\) 和 \(p_{k+1},p_{k+2},\dots,p_{m}\) 也必须是合法序列。

形式化地说,\(p_1,p_2,\dots,p_m\) 必须是一个大根堆的中序遍历。

这样就可以 \(dp\) 了。设 \(dp_{a,b,c,x1,x2}\) 表示起点为 \(a\),终点为 \(b\),按下的按键的编号都 \(\leq c\) 的合法的行走路线个数。\(x1\) 表示是否对第一次的按键有要求,\(x2\) 表示是否对最后一次的按键有要求。

先考虑 \(x1=x2=0\) 的情况,即对起点和终点按键都没有要求。

那么有以下两种转移方式:

  • 按键序列中按下的编号最大的按键 \(<c\),则 \(dp_{a,b,c,0,0}=dp_{a,b,c-1,0,0}\)
  • 若按键序列中按下的编号最大的按键 \(=c\),那么我们枚举在 \(k\) 点按下编号为 \(c\) 的键,那么 \(a\) 到 \(b\) 的路径被我们拆成了两部分 \(a\to k\) 和 \(k\to b\)。我们进一步枚举 \(k\) 之前到达的点 \(u\) 和 \(k\) 接下来到达的点 \(v\),那么根据之前的推论,\(a\to u\) 路径上我们只能按下编号 \(\leq c-1\) 的键,\(v\to b\) 的路径上我们也只能按下编号 \(\leq c-1\) 的键。故我们有 \(dp_{a,b,c,0,0}=\sum\limits_{k}\sum\limits_{(u,k)}\sum\limits_{(k,v)}dp_{a,u,c-1,0,0}\times dp_{v,b,c-1,0,0}\),记 \(f_{a,k,c}=\sum\limits_{(u,k)}dp_{a,u,c-1,0,0}\), \(g_{b,k,c}=\sum\limits_{(k,v)}dp_{v,b,c-1,0,0}\),那么上述式子可优化为 \(dp_{a,b,c,0,0}=\sum\limits_{k}f_{a,k,c}\times g_{b,k,c}\)(当然,如果 \(k=a\) 那么 \(k\) 可以是路径当中第一个点,此时就不存在 \(k\) 之前的点了,故令所有 \(f_{a,a,c}\) 加 \(1\) 即可,\(g_{b,b,c}\) 同理)。

紧接着是 \(x1\neq 0\) 或 \(x2\neq 0\) 的情况,这里以 \(x1=1,x2=0\) 的情况为例,其它两种情况同理。

还是分两种情况(其实与之前那种情况大差不差):

  • 按键序列中按下的编号最大的按键 \(<c\),则 \(dp_{a,b,c,1,0}=dp_{a,b,c-1,1,0}\)。
  • 若按键序列中按下的编号最大的按键 \(=c\), \(dp_{a,b,c,1,0}=\sum\limits_{k}\sum\limits_{(u,k)}\sum\limits_{(k,v)}dp_{a,u,c-1,1,0}\times dp_{v,b,c-1,0,0}\),你还是记 \(f_{a,k,c}=\sum\limits_{(u,k)}dp_{a,u,c-1,1,0}\), \(g_{b,k,c}=\sum\limits_{(k,v)}dp_{v,b,c-1,0,0}\),将上述式子优化为 \(dp_{a,b,c,1,0}=\sum\limits_{k}f_{a,k,c}\times g_{b,k,c}\)(只不过 \(k=a\) 的情况要改一下,只有当 \(c=bs\) 的时候才能令 \(f_{a,a,c}\) 加 \(1\))

这样 \(dp\) 一遍是 \(n^4\) 的,加上 \(q\) 次询问的条件,显然不能让我们满意。

不过发现对于每组询问,只有当 \(a=s\) 的时候 \(dp_{a,b,c,1,0}\) 才有意义,只有当 \(b=t\) 的时候 \(dp_{a,b,c,0,1}\) 才有意义。所以可以预处理出 \(dp_{a,b,c,0,0}\) 的值,对于每组询问重新计算 \(dp_{a,b,c,0,1},dp_{a,b,c,1,0},dp_{a,b,c,1,1}\) 的值。这样总复杂度就降到了 \(n^4+qn^3\)。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#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;
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=1;
while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
x*=neg;
}
const int MAXN=60;
const int MOD=1e9+7;
int n,k,qu;
char ed[MAXN+5][MAXN+5];
int dp[2][2][MAXN+5][MAXN+5][MAXN+5],f[2][MAXN+5][MAXN+5][MAXN+5],g[2][MAXN+5][MAXN+5][MAXN+5];
void prework(){
for(int x=1;x<=k;x++){
for(int i=1;i<=n;i++) for(int k=1;k<=n;k++) for(int l=1;l<=n;l++)
if(ed[l][k]=='1') f[0][i][k][x]=(f[0][i][k][x]+dp[0][0][i][l][x-1])%MOD;
for(int j=1;j<=n;j++) for(int k=1;k<=n;k++) for(int l=1;l<=n;l++)
if(ed[k][l]=='1') g[0][j][k][x]=(g[0][j][k][x]+dp[0][0][l][j][x-1])%MOD;
for(int i=1;i<=n;i++) f[0][i][i][x]=(f[0][i][i][x]+1)%MOD;
for(int i=1;i<=n;i++) g[0][i][i][x]=(g[0][i][i][x]+1)%MOD;
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dp[0][0][i][j][x]=dp[0][0][i][j][x-1];
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) for(int k=1;k<=n;k++)
dp[0][0][i][j][x]=(dp[0][0][i][j][x]+1ll*f[0][i][k][x]*g[0][j][k][x]%MOD)%MOD;
}
}
int query(int s,int bs,int t,int bt){
fill0(dp[1][0]);fill0(dp[0][1]);fill0(dp[1][1]);fill0(f[1]);fill0(g[1]);
for(int x=1;x<=k;x++){
for(int k=1;k<=n;k++) for(int l=1;l<=n;l++)
if(ed[l][k]=='1') f[1][s][k][x]=(f[1][s][k][x]+dp[1][0][s][l][x-1])%MOD;
for(int k=1;k<=n;k++) for(int l=1;l<=n;l++)
if(ed[k][l]=='1') g[1][t][k][x]=(g[1][t][k][x]+dp[0][1][l][t][x-1])%MOD;
if(x==bs) f[1][s][s][x]=(f[1][s][s][x]+1)%MOD;
if(x==bt) g[1][t][t][x]=(g[1][t][t][x]+1)%MOD;
for(int i=1;i<=n;i++){
dp[1][0][s][i][x]=dp[1][0][s][i][x-1];
dp[0][1][i][t][x]=dp[0][1][i][t][x-1];
for(int k=1;k<=n;k++){
dp[1][0][s][i][x]=(dp[1][0][s][i][x]+1ll*f[1][s][k][x]*g[0][i][k][x]%MOD)%MOD;
dp[0][1][i][t][x]=(dp[0][1][i][t][x]+1ll*f[0][i][k][x]*g[1][t][k][x]%MOD)%MOD;
}
}
dp[1][1][s][t][x]=dp[1][1][s][t][x-1];
for(int k=1;k<=n;k++){
dp[1][1][s][t][x]=(dp[1][1][s][t][x]+1ll*f[1][s][k][x]*g[1][t][k][x]%MOD)%MOD;
}
}
return dp[1][1][s][t][k];
}
int main(){
scanf("%d%d%d",&n,&k,&qu);
for(int i=1;i<=n;i++) scanf("%s",ed[i]+1);
prework();
while(qu--){
int s,bs,t,bt;scanf("%d%d%d%d",&bs,&s,&bt,&t);
printf("%d\n",query(s,bs,t,bt));
}
return 0;
}
/*
6 3 8
010000
001000
000100
000010
000000
000001
1 1 1 1
3 3 1 1
1 1 3 3
1 1 1 5
2 1 1 5
1 1 2 5
3 1 3 5
2 6 2 6
*/

洛谷 P7155 [USACO20DEC] Spaceship P(dp)的更多相关文章

  1. 洛谷 P5279 - [ZJOI2019]麻将(dp 套 dp)

    洛谷题面传送门 一道 dp 套 dp 的 immortal tea 首先考虑如何判断一套牌是否已经胡牌了,考虑 \(dp\)​​​​​.我们考虑将所有牌按权值大小从大到小排成一列,那我们设 \(dp_ ...

  2. 洛谷2344 奶牛抗议(DP+BIT+离散化)

    洛谷2344 奶牛抗议 本题地址:http://www.luogu.org/problem/show?pid=2344 题目背景 Generic Cow Protests, 2011 Feb 题目描述 ...

  3. Lightning Conductor 洛谷P3515 决策单调性优化DP

    遇见的第一道决策单调性优化DP,虽然看了题解,但是新技能√,很开森. 先%FlashHu大佬,反正我是看了他的题解和精美的配图才明白的,%%%巨佬. 废话不多说,看题: 题目大意 已知一个长度为n的序 ...

  4. 洛谷P1541 乌龟棋(四维DP)

    To 洛谷.1541 乌龟棋 题目背景 小明过生日的时候,爸爸送给他一副乌龟棋当作礼物. 题目描述 乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数).棋盘第1格是唯一的起点,第N格是终点,游 ...

  5. 【洛谷】P1052 过河【DP+路径压缩】

    P1052 过河 题目描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙 ...

  6. 【题解】洛谷P1052 [NOIP2005TG] 过河(DP+离散化)

    题目来源:洛谷P1052 思路 一开始觉得是贪心 但是仔细一想不对 是DP 再仔细一看数据不对 有点大 如果直接存下的话 显然会炸 那么就需要考虑离散化 因为一步最大跳10格 那么我们考虑从1到10都 ...

  7. 洛谷1736(二维dp+预处理)

    洛谷1387的进阶版,但很像. 1387要求是“全为1的正方形”,取dp[i][j] = min(dp[i-1][j-1], min(dp[i-1][j], dp[i][j-1]))吧?这个有“只有对 ...

  8. 【洛谷4933】大师(DP)

    题目: 洛谷4933 分析: (自己瞎yy的DP方程竟然1A了,写篇博客庆祝一下) (以及特斯拉电塔是向Red Alert致敬吗233) 这里只讨论公差不小于\(0\)的情况,小于\(0\)的情况进行 ...

  9. C++ 洛谷 2014 选课 from_树形DP

    洛谷 2014 选课 没学树形DP的,看一下. 首先要学会多叉树转二叉树. 树有很多种,二叉树是一种人人喜欢的数据结构,简单而且规则.但一般来说,树形动规的题目很少出现二叉树,因此将多叉树转成二叉树就 ...

随机推荐

  1. Pytorch——张量 Tensors

    张量 Tensors 1.torch.is_tensor torch.is_tensor(obj) 用法:判断是否为张量,如果是 pytorch 张量,则返回 True. 参数:obj (Object ...

  2. Unity 3D手游对不同分辨率屏幕的UI自适应

    目前安卓手机的屏幕大小各异,没有统一的标准,因此用Unity 3D制作的手游需要做好对不同分辨率屏幕的UI自适应,否则就会出现UI大小不一和位置错位等问题. 我们的项目在开发时的参照分辨率(Refer ...

  3. DDD领域驱动设计-案例建模设计-Ⅲ

    1. 背景 参考<DDD领域驱动设计-案例需求文档>,本文将构建实体,聚合根详述领域驱动中的建模设计.构建实体,聚合根的一些原则或方法,将在后续文章中说明. 2. 建模设计 2.1. 实体 ...

  4. C语言基础知识总结大全

    1.入门程序 #include <stdio.h> int main() { printf("Hello World!"); return 0; } 2.数据类型 数据 ...

  5. C语言基础资料,可以看看哦

    C语言程序的结构认识 用一个简单的c程序例子,介绍c语言的基本构成.格式.以及良好的书写风格,使小伙伴对c语言有个初步认识. 例1:计算两个整数之和的c程序: #include main() { in ...

  6. Sharding-JDBC基本使用,整合Springboot实现分库分表,读写分离

    结合上一篇docker部署的mysql主从, 本篇主要讲解SpringBoot项目结合Sharding-JDBC如何实现分库分表.读写分离. 一.Sharding-JDBC介绍 1.这里引用官网上的介 ...

  7. Spark面试题整理(三)

    1.为什么要进行序列化序列化? 可以减少数据的体积,减少存储空间,高效存储和传输数据,不好的是使用的时候要反序列化,非常消耗CPU. 2.Yarn中的container是由谁负责销毁的,在Hadoop ...

  8. IDA*、操作打表、并行处理-The Rotation Game HDU - 1667

    万恶之源 优秀题解 用文字终究难以穷尽代码的思想 思路 每次操作都有八种选择,相当于一棵每次延申八个子节点的搜索树,故搜索应该是一种方法.而这题要求求最少步数,我们就可以想到可以试试迭代加深搜索(但其 ...

  9. Spring---IoC(控制反转)原理学习笔记【全】

    1.IoC创建对象的方式 使用无参构造创建对象 假如要使用有参构造创建: 下标赋值constructor-arg <!--有参--> <bean id="User" ...

  10. CSS学习(三)特指度和层叠

    一.特指度 特制度的一般形式是0,0,0,0 行内样式,第一位的特指度加一 id选择符,第二位的特指度加一 类选择符.属性选择符.伪类,第三位的特指度加一 元素选择符.伪元素,第四位的特指度加一 特指 ...