题意:有一张白纸条,你需要给这张纸条染色。染色从颜色1开始染色,每次选择纸条的一段染色时,这一段的颜色必须是相同的。现在给你染色后的纸条,问有多少种染色方案?

F1: 思路:最开始的想法是以染色顺序为一个维度,然后染色区间为另外两个维度去DP,但是最后发现不可以,因为之前的所有的染色对后面的影响不确定,只用在染第几种颜色是无法确定现在可以染色的区间的,还无法记忆化,只能去看题解。。。官方题解的DP设计的比较巧妙。我们DP并不关心染色顺序,我们的关注点在区间。我们可以发现,对于一个区间,第一次开始染的颜色一定是标号最小的那种颜色。假设当前区间是[l, r],标号最小的颜色c的位置是p,并且在这个区间内染上颜色c的区间是[a, b],那么区间被分为了4部分:[l, a - 1], [a, p - 1], [p + 1, b], [b + 1, r],递归的求解这四部分,再把答案乘起来就可以了。容易发现a和b的枚举是独立的,那么把枚举的a的答案和枚举的b的答案分别求出,乘起来就可以了。

代码:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define pii pair<int, int>
#define db double
#define LL long long
using namespace std;
const int maxn = 510;
const LL mod = 998244353;
LL dp[maxn][maxn];
int n, m;
pii c[maxn];
set<int> s, s1;
set<int> ::iterator it;
int L[maxn], R[maxn];
int cnt[maxn];
int mi[maxn][maxn];
LL dfs(int l, int r) {
if(r <= l) return 1;
if(dp[l][r] != -1) return dp[l][r];
int p = mi[l][r];
LL sum1 = 0, sum2 = 0;
for (int i = l - 1; i < p; i++) {
sum1 += (dfs(l, i) * dfs(i + 1, p - 1)) % mod;
}
sum1 %= mod;
for (int j = p; j <= r; j++) {
sum2 += (dfs(p + 1, j) * dfs(j + 1, r)) % mod;
}
sum2 %= mod;
dp[l][r] = (sum1 * sum2) % mod;
return dp[l][r];
}
int main() {
scanf("%d%d", &n, &m);
memset(dp, -1, sizeof(dp));
for (int i = 1; i <= n; i++) {
scanf("%d", &c[i].first);
c[i].second = i;
L[i] = 1, R[i] = n;
}
s.insert(0);
s.insert(n + 1);
sort(c + 1, c + 1 + n);
for (int i = 1; i <= n; i++) {
L[i] = (*(--s.lower_bound(c[i].second)));
R[i] = (*s.lower_bound(c[i].second));
L[i]++, R[i]--;
for (int j = L[i]; j <= c[i].second; j++)
for (int k = c[i].second; k <= R[i]; k++)
mi[j][k] = c[i].second;
s.insert(c[i].second);
}
cout << dfs(1, n) << endl;
}

F2: 现在纸条的范围是1e6,不能直接区间DP了。不过通过观察发现,如果纸条上有多个相邻的位置颜色相同,那么可以缩成一个位置,因为这些位置整体要么都不覆盖,要么全部覆盖,和一个位置的贡献相同。容易发现如果方案合法缩点之后点数不会超过1000,所有超过1000的可以直接输出0。缩点之后和F1已经很像了,不过有些小细节需要注意。首先需要判断方案是否合法,容易发现如果有两个颜色相同的位置中间有标号比它们小的颜色,方案不合法。这个直接暴力判断就可以了。其次,在DP计算方案时,除了F1中的四个部分,区间内有可能有多最小标号的位置,这些位置之间的区间也要乘起来。最后,我们不能直接像F1那样枚举区间,比如这种例子:

8 10
8 4 3 5 7 5 2 6 2 1

容易发现,我们在枚举2的覆盖区间时,覆盖区间的左端点不能在2个5之间(包括最右边的5),如果2在这里覆盖了,5这种情况是不可能出现的。这个用指针之类的处理一下就可以了。

代码:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define pii pair<int, int>
#define db double
#define LL long long
using namespace std;
const int maxn = 1010;
const LL mod = 998244353;
LL dp[maxn][maxn];
int n, m;
int c[1000010];
int L[maxn], R[maxn];
int cnt[maxn], last[maxn], Next[maxn];
int mi[maxn][maxn];
vector<pii> re[maxn];
LL dfs(int l, int r) {
if(r <= l) return 1;
if(dp[l][r] != -1) return dp[l][r];
int lp = L[mi[l][r]], rp = R[mi[l][r]];
LL sum1 = 0, sum2 = 0, sum3 = 1;
int now = mi[l][r];
for (int i = 0; i < re[now].size(); i++) {
sum3 = (sum3 * dfs(re[now][i].first, re[now][i].second)) % mod;
}
for (int i = l - 1; i < lp; i = Next[i]) {
sum1 += (dfs(l, i) * dfs(i + 1, lp - 1)) % mod;
}
sum1 %= mod;
for (int j = rp; j <= r; j = Next[j]) {
sum2 += (dfs(rp + 1, j) * dfs(j + 1, r)) % mod;
}
sum2 %= mod;
dp[l][r] = (((sum1 * sum2) % mod) * sum3) % mod;
return dp[l][r];
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
scanf("%d", &c[i]);
}
int tot = 0;
tot = 1;
for (int i = 2; i <= m; i++) {
if(c[i] == c[i - 1]) {
continue;
} else {
c[++tot] = c[i];
}
}
if(tot > 1000) {
printf("0\n");
return 0;
}
memset(dp, -1, sizeof(dp));
memset(last, -1, sizeof(last));
for (int i = 1; i <= tot; i++) {
mi[i][i] = c[i];
for (int j = i + 1; j <= tot; j++) {
mi[i][j] = min(c[j], mi[i][j - 1]);
}
L[i] = tot + 1;
R[i] = 0;
}
bool flag = 0;
for (int i = 1; i <= tot; i++) {
L[c[i]] = min(L[c[i]], i);
R[c[i]] = max(R[c[i]], i);
if(last[c[i]] != -1) {
if(mi[last[c[i]] + 1][i - 1] < c[i]) {
flag = 1;
break;
}
re[c[i]].push_back(make_pair(last[c[i]] + 1, i - 1));
}
last[c[i]] = i;
}
for (int i = 0; i <= tot; i++) {
Next[i] = R[c[i + 1]];
}
Next[tot] = tot + 1;
if(flag) {
printf("0\n");
return 0;
}
cout << dfs(1, tot) << endl;
return 0;
}

  

Codeforces 1178F DP的更多相关文章

  1. Two Melodies CodeForces - 813D (DP,技巧)

    https://codeforces.com/problemset/problem/813/D dp[i][j] = 一条链以i结尾, 另一条链以j结尾的最大值 关键要保证转移时两条链不能相交 #in ...

  2. Consecutive Subsequence CodeForces - 977F(dp)

    Consecutive Subsequence CodeForces - 977F 题目大意:输出一序列中的最大的连续数列的长度和与其对应的下标(连续是指 7 8 9这样的数列) 解题思路: 状态:把 ...

  3. codeforces的dp专题

    1.(467C)http://codeforces.com/problemset/problem/467/C 题意:有一个长为n的序列,选取k个长度为m的子序列(子序列中不能有位置重复),求所取的k个 ...

  4. Codeforces 721C [dp][拓扑排序]

    /* 题意:给你一个有向无环图.给一个限定t. 问从1点到n点,在不超过t的情况下,最多可以拜访几个点. 保证至少有一条路时限不超过t. 思路: 1.由无后向性我们可以知道(取决于该图是一个DAG), ...

  5. CodeForces 607C (DP) Hard problem

    题目:这里 题意:给定n个字符串,每个字符串可以进行一项操作,就是将这个字符串交换,就是该字符串的第一个和最后一个交换,第二个和倒数第二个交换,以此类推,当然可以选择对于 该字符串进行或不进行这项操作 ...

  6. Codeforces 611d [DP][字符串]

    /* 题意:给一个长度不超过5000的字符串,每个字符都是0到9的数字. 要求将整个字符串划分成严格递增的几个数字,并且不允许前导零. 思路: 1.很开心得发现,当我在前i个区间以后再加一个区间的时候 ...

  7. Codeforces 404D [DP]

    /* 我是一个习惯后悔,但是没办法忍受内疚感的二货== 这题是个无脑dp,但是比赛大概20min没出...其实最后5min我好好想想简单化边界条件,可以出的. 题意: 给你一个长度为1e6的由?*01 ...

  8. Codeforces 119C DP

    题意: 有n天,m门课和常数k; 每天上一门课,每门课程有两个属性,最少作业量a,最多作业量b,和难度c. 1<=a<=b<=1e16 c<=100 1<=n<=m ...

  9. Codeforces 1096F(dp + 树状数组)

    题目链接 题意: 对于长度为$n$的排列,在已知一些位的前提下求逆序对的期望 思路: 将答案分为$3$部分 $1.$$-1$与$-1$之间对答案的贡献.由于逆序对考虑的是数字之间的大小关系,故假设$- ...

随机推荐

  1. 洛谷3321 SDOI2015 序列统计

    懒得放传送[大雾 有趣的一道题 前几天刚好听到Creed_神犇讲到相乘转原根变成卷积的形式 看到这道题当然就会做了啊w 对于m很小 我们暴力找原根 如果你不会找原根的话 出门左转百度qwq 找到原根以 ...

  2. 什么是NFA(不确定的有穷自动机)和DFA(确定的有穷自动机)

    本节知识点是<编译原理>第三章-词法分析,学习参考教材为清华大学出版社<编译原理>第三版: 前情提要: 字母表∑1和∑2的乘积( product): ∑1∑2 ={ab|a ∈ ...

  3. Python 石头 剪刀 布

    di = {1: '石头', 2: '剪刀', 3: '布'} def win(x, y): if len({x[0], y[0]}) == 1: print('平局.') else: if {x[0 ...

  4. substr()、substring()、slice()

    substr(start,length) start(必选)开始位置的下标 可为负数-1即为倒数第一个字符以此类推 0为第一个字母下标 length长度(可选)如果省略该参数则默认到最后一位 var ...

  5. matlab中求解线性方程组的rref函数

    摘自:http://www.maybe520.net/blog/987/ matlab中怎么求解线性方程组呢? matlab中求解线性方程组可应用克拉默法则(Cramer's Rule)即通过det( ...

  6. 分布式架构的CAP原理

    CAP 定理的含义   一.分布式系统的三个指标 1998年,加州大学的计算机科学家 Eric Brewer 提出,分布式系统有三个指标. Consistency Availability Parti ...

  7. SpringMVC的 ModelAndView

    使用ModelAndView类用来存储处理完后的结果数据,以及显示该数据的视图. public class ModelAndView { /** View instance or view name ...

  8. UILabel How to set background image

    UILabel *myLabel=[[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 20)]; UIImage *img = [UIImage ...

  9. 【系统架构理论】一篇文章精通:Spring Cloud Netflix Eureka

    是官方文档的总结 http://spring.io/projects/spring-cloud-netflix#overview 讲解基于2.0.2版本官方文档 https://cloud.sprin ...

  10. Design:设计(活动)百科

    ylbtech-Design:设计(活动)百科 设计是把一种设想通过合理的规划.周密的计划.通过各种感觉形式传达出来的过程.人类通过劳动改造世界,创造文明,创造物质财富和精神财富,而最基础.最主要的创 ...