HDU6578链接

题意

有一串字符串,仅由 {0,1,2,3}\{0, 1, 2, 3\}{0,1,2,3} 组成,长度为 nnn,同时满足 mmm 个条件。每个条件由三个整数组成:l、r、xl、r、xl、r、x 表示在这个字符串的 [l,r][l, r][l,r] 这个区间内,有且仅有 xxx 个不同的字符,求问可能的组合有多少种(mod 998244353)

分析题意

因为前几天刚刚写了牛客暑期多校第二场,其中有一道题:ABBA(我的题解)感觉有点接近,所以第一想法就是dp了。但是这道题的字符多,不能像ABBA一样压缩至一维。所以只能想想看当时牛客官方给的题解的方法了。

考虑到之后需要判断条件是否满足,所以我第一感觉就是得定义一个超多维度的dp数组:

const int MAXN = 110;
long long dp[MAXN][MAXN][MAXN][MAXN][MAXN];

各个维度的定义如下:

对于 dp[a][b][c][d][t]

表示整个字符串长度为 t ,最后一次出现 0 的位置为 a,最后一次出现 1 的位置为 b ,最后一次出现 2 的位置为 c ,最后一次出现 3 的位置为d

然后可以得到状态转移方程

dp[t + 1][b][c][d][t + 1] += dp[a][b][c][d][t];
dp[a][t + 1][c][d][t + 1] += dp[a][b][c][d][t];
dp[a][b][t + 1][d][t + 1] += dp[a][b][c][d][t];
dp[a][b][c][t + 1][t + 1] += dp[a][b][c][d][t];

当然这个数组肯定是没法开的,所以把 t 压缩了,变成滚动dp

const int MAXN = 110;
long long dp[MAXN][MAXN][MAXN][MAXN][2];

然后通过滚动的方式来实现。

但是这并不是卡死这种方法的原因。

根据上面这些,可以写出整个dp过程,大概就是这样:

for (int t = 1; t <= n; t++)
{
for (int a = 0; a <= t; a++)
for (int b = 0; b <= t; b++)
for (int c = 0; c <= t; c++)
for (int d = 0; d <= t; d++)
dp[a][b][c][d][t & 1] = 0;
for (int a = 0; a <= t; a++)
for (int b = 0; b <= t; b++)
for (int c = 0; c <= t; c++)
for (int d = 0; d <= t; d++)
{
dp[t + 1][b][c][d][t & 1] += dp[a][b][c][d][t ^ 1];
dp[a][t + 1][c][d][t & 1] += dp[a][b][c][d][t ^ 1];
dp[a][b][t + 1][d][t & 1] += dp[a][b][c][d][t ^ 1];
dp[a][b][c][t + 1][t & 1] += dp[a][b][c][d][t ^ 1];
}
}

这个复杂的……这还没加上判断是否满足条件……

无论是时间是还是空间上,估计都悬(时间上应该是一定过不了了)

然后只能继续压缩。

考虑到最后无论哪种状态下,a,b,c,da, b, c, da,b,c,d 四个变量中,必定有一个且仅有一个变量的值为 t 。而且在这道题中,字符 {0,1,2,3}\{0, 1, 2, 3\}{0,1,2,3} 完全等价。所以我们再压缩一维

const int MAXN = 110;
long long dp[MAXN][MAXN][MAXN][2];

此时的意义如下:

对于变量 dp[x][y][z]

表示字符 {0,1,2,3}\{0, 1, 2, 3\}{0,1,2,3} 中,其中一个最后出现的位置为当前的字符最后(由于这个条件恒成立,所以并未被记录在数组中),剩下的三个字符分别出现在x,y,zx, y, zx,y,z处,且保证 i<x≤y≤z(i为当前字符长度)i < x \leq y \leq z (i 为当前字符长度)i<x≤y≤z(i为当前字符长度) (仅当 x=y=0x = y = 0x=y=0 时满足前面一个等于号,后面的等于号同理。而字符串长度至少为1,且此时x、y、zx、y、zx、y、z均为0,所以不存在 i=xi = xi=x 的情况。)而后面的长度为 2 的维度指代当前状态和 上一个状态(滚动dp)

可以得到状态转移方程:

// i 为当前字符串长度,cur 为当前状态,last 为上一个状态
dp[x][y][z][cur] += dp[x][y][z][last]; // 加入的字符与上一个加入的字符相同
dp[i - 1][y][z][cur] += dp[x][y][z][last];
dp[i - 1][x][z][cur] += dp[x][y][z][last];
dp[i - 1][x][y][cur] += dp[x][y][z][last];

得到整个 dp 过程:

dp[0][0][0][0] = 1;
int cur = 1;
int last = 0;
for (int i = 1; i <= n; i++)
for (int x = 0; x <= i; x++)
for (int y = 0; y <= x; y++)
for (int z = 0; z <= y; z++)
dp[x][y][z][cur] = 0;
for (int x = 0; x < i; x++)
for (int y = 0; y <= x; y++)
for (int z = 0; z <= y; z++)
{
dp[x][y][z][cur] += dp[x][y][z][last];
dp[i - 1][y][z][cur] += dp[x][y][z][last];
dp[i - 1][x][z][cur] += dp[x][y][z][last];
dp[i - 1][x][y][cur] += dp[x][y][z][last]; dp[x][y][z][cur] %= mod; // 别忘了 mod
dp[i - 1][y][z][cur] %= mod;
dp[i - 1][x][z][cur] %= mod;
dp[i - 1][x][y][cur] %= mod;
}
swap(cur, last);
}

考虑条件

需要判断一个区间内是否满足有多个不同的字符

我们可以根据区间右端为基准,当前dp的字符串长度到达一个条件的右端的时候,通过 x、y、zx、y、zx、y、z 的值来判断是否到达了要求,如果没有则将此 dp 的赋值为0。如果懒得思考可以直接分类讨论一下就行了。虽然代码会比较长。

AC代码

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 110;
const int mod = 998244353; long long dp[MAXN][MAXN][MAXN][2];
// dp[i][j][k] 表示上一次出现不同数字的位置分别是 i、j、k、当前位置 struct Conditions
{
int l, x;
Conditions(int ll, int xx) : l(ll), x(xx) {}
}; vector<Conditions> conditions[MAXN]; // 用来保存要求 int main()
{
#ifdef ACM_LOCAL
freopen("./in.txt", "r", stdin);
freopen("./out.txt", "w", stdout);
#endif
ios::sync_with_stdio(false);
int t;
cin >> t;
while (t--)
{
int n, m;
cin >> n >> m;
int a, b, c;
for (int i = 0; i < MAXN; i++)
{
conditions[i].clear();
}
for (int i = 0; i < m; i++)
{
cin >> a >> b >> c;
conditions[b].push_back(Conditions(a, c)); // 要求按照 r 的不同来保存
}
dp[0][0][0][0] = 1;
int cur = 1;
int last = 0;
for (int i = 1; i <= n; i++)
{
for (int x = 0; x <= i; x++)
{
for (int y = 0; y <= x; y++)
{
for (int z = 0; z <= y; z++)
{
dp[x][y][z][cur] = 0;
}
}
}
for (int x = 0; x < i; x++)
{
for (int y = 0; y <= x; y++)
{
for (int z = 0; z <= y; z++)
{
dp[x][y][z][cur] += dp[x][y][z][last];
dp[i - 1][y][z][cur] += dp[x][y][z][last];
dp[i - 1][x][z][cur] += dp[x][y][z][last];
dp[i - 1][x][y][cur] += dp[x][y][z][last]; dp[x][y][z][cur] %= mod;
dp[i - 1][y][z][cur] %= mod;
dp[i - 1][x][z][cur] %= mod;
dp[i - 1][x][y][cur] %= mod;
}
}
}
for (int s = 0; s < conditions[i].size(); s++)
{
for (int x = 0; x < i; x++)
{
for (int y = 0; y <= x; y++)
{
for (int z = 0; z <= y; z++)
{
int cnt = 1 + (x >= conditions[i][s].l ? 1 : 0) + (y >= conditions[i][s].l ? 1 : 0) + (z >= conditions[i][s].l ? 1 : 0); // 判断剩下三个位置是否满足条件
if (cnt != conditions[i][s].x)
dp[x][y][z][cur] = 0;
}
}
}
}
swap(cur, last);
}
long long ans = 0; // 求算最终答案。需要把所有的情况都加起来
for (int x = 0; x < n; x++)
{
for (int y = 0; y <= x; y++)
{
for (int z = 0; z <= y; z++)
{
ans += dp[x][y][z][last];
ans %= mod;
}
}
}
cout << ans << endl;
}
return 0;
}

【2019多校第一场补题 / HDU6578】2019多校第一场A题1001Blank——dp的更多相关文章

  1. HDU6578 2019HDU多校训练赛第一场 1001 (dp)

    HDU6578 2019HDU多校训练赛第一场 1001 (dp) 传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6578 题意: 你有n个空需要去填,有 ...

  2. hdu6578 2019湖南省赛D题Modulo Nine 经典dp

    目录 题目 解析 AC_Code @ 题目 第一题题意是一共有{0,1,2,3}四种数字供选择,问有多少个长度为n的序列满足所有m个条件,每个条件是说区间[L,R]内必须有恰好x个不同的数字. 第二题 ...

  3. NOI.AC NOIP模拟赛 第一场 补记

    NOI.AC NOIP模拟赛 第一场 补记 candy 题目大意: 有两个超市,每个超市有\(n(n\le10^5)\)个糖,每个糖\(W\)元.每颗糖有一个愉悦度,其中,第一家商店中的第\(i\)颗 ...

  4. NOI.AC NOIP模拟赛 第四场 补记

    NOI.AC NOIP模拟赛 第四场 补记 子图 题目大意: 一张\(n(n\le5\times10^5)\)个点,\(m(m\le5\times10^5)\)条边的无向图.删去第\(i\)条边需要\ ...

  5. 2019年第一天——使用Visual Studio 2019 Preview创建第一个ASP.Net Core3.0的App

    一.前言: 全文翻译自:https://www.talkingdotnet.com/creating-first-asp-net-core-3-0-app-visual-studio-2019/ Vi ...

  6. 计蒜客 NOIP 提高组模拟竞赛第一试 补记

    计蒜客 NOIP 提高组模拟竞赛第一试 补记 A. 广场车神 题目大意: 一个\(n\times m(n,m\le2000)\)的网格,初始时位于左下角的\((1,1)\)处,终点在右上角的\((n, ...

  7. NOI.AC NOIP模拟赛 第二场 补记

    NOI.AC NOIP模拟赛 第二场 补记 palindrome 题目大意: 同[CEOI2017]Palindromic Partitions string 同[TC11326]Impossible ...

  8. NOI.AC NOIP模拟赛 第三场 补记

    NOI.AC NOIP模拟赛 第三场 补记 列队 题目大意: 给定一个\(n\times m(n,m\le1000)\)的矩阵,每个格子上有一个数\(w_{i,j}\).保证\(w_{i,j}\)互不 ...

  9. 剑指offer35题:第一个只出现一次的字符+剑指offer55题:字符流中第一个不重复的字符+剑指offer51题:数组中重复的数字

    在看剑指offer的时候,感觉这三个题目很像,都是用哈希表可以解决,所以把这三个题整理出来,以供复习. 剑指offer35题:第一个只出现一次的字符 题目描述:在字符串中找出第一个只出现一次的字符.如 ...

  10. Azure DevOps Server 2019 第一个补丁包(2019.0.1 RTW)

    在Azure DevOps Server 2019正式发布后的2周左右时间,微软快速发布了第一个补丁包Azure DevOps Server 2019.0.1 RTW.Azure DevOps Ser ...

随机推荐

  1. SQL JOIN 和 UNION 用法

    1 SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo FROM Persons, Orders WHERE Persons.Id_P ...

  2. 安卓权威编程指南-笔记(第21章 XML drawable)

    在Andorid的世界里,凡事要在屏幕上绘制的东西都可以叫drawable,比如抽象图形,Drawable的子类,位图图形等,我们之前用来封装图片的BitmapDrawable就是一种drawable ...

  3. Flutter Widgets 之 BottomNavigationBar 和 BottomNavigationBarItem

    注意:无特殊说明,Flutter版本及Dart版本如下: Flutter版本: 1.12.13+hotfix.5 Dart版本: 2.7.0 BottomNavigationBar 和 BottomN ...

  4. OO第四单元总结暨学期总结

    一.第四单元作业架构设计 我们第四单元围绕UML图展开,在第四单元开始之前,本来以为我们的工作是学习如何使用UML工具,开始后才意识到我们要做的是解析UML类图.顺序图和状态图.当然,让我们解析的只是 ...

  5. vs2017 tfs服务器迁移更换服务器IP地址方法

    今天公司服务器换了IP地址,然后发现tfs的服务器删除不了,也添加不了.最后参考了其他vs版本提供的方法,找到了解决的方法. 一共需要修改两个地方: 1.找到项目的sln文件,使用其他文本编辑器打开, ...

  6. swoole(1)使用docker安装swoole环境

    1.下载镜像 pull php 镜像 docker pull php:7.3-alpine3.8 创建容器 docker run -it --name test php:7.3-alpine3.8 s ...

  7. 如何在windows server上安装 Windows评估和部署工具包

    此文是<.NET内存宝典>一书的售后服务系列文章之一. 在<.NET内存宝典>一书(目前我还在翻译本书,预计年底出版)的第3章 “内存测量”里的“Windows性能工具包”一节 ...

  8. 分享到微信,QQ等各大网络媒体网站代码

    http://www.jiathis.com/ 打开此网站,如果没有账号,请注册一下,然后登陆账号,进入网页以后直接可以复制代码到页面的标签,进行css样式布局,直接可以在页面测试,如果方便的话直接百 ...

  9. CSS-水平居中、垂直居中、水平垂直居中

    1.水平居中 水平居中可分为行内元素水平居中和块级元素水平居中 1.1 行内元素水平居中 这里行内元素是指文本text.图像img.按钮超链接等,只需给父元素设置text-align:center即可 ...

  10. new Date在IOS下面的兼容问题

    此问题坑爹啊,着实坑爹,要不是本宝宝鬼机灵再次进行了测试,不然测试都测不出来的问题,问题源头,有两个时间: let start =  "2018-08-08 00:00:00" ; ...