2019HDU多校第一场 BLANK DP

题意:有四种数字,现在有若干个限制条件:每个区间中不同的数字种类必须是多少种,问合法的方案数。

思路: 定义 dp[i][j][k][t] 代表填完前 t 个位置后,{0,1,2,3} 这 4 个数字最后一次出现的位置, 排序后为 i,j,k,t(i < j < k < t) 的方案数目,则按照第 t+1 位的数字的四种选择,可以得 到四种转移。 对于限制可以按照限制区间的右端点分类,求出 dp[i][j][k][t] 后,找到所有以 t 为区间 右端点的限制条件,如果当前状态不满足所有限制条件则不合法,不再向后转移。 总时间复杂度 O(n4)。滚动一维,空间复杂度 O(n3)

代码:

#include <bits/stdc++.h>
#define pii pair<int, int>
using namespace std;
const int maxn = 101;
const int mod = 998244353;
int dp[2][maxn][maxn][maxn];
vector<pii> re[maxn];
int main() {
	int T, x, y, z, n, m;
	scanf("%d", &T);
	while(T--) {
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; i++)
			re[i].clear();
		for (int i = 1; i <= n; i++)
			re[i].clear();
		for (int i = 1; i <= m; i++) {
			scanf("%d%d%d", &x, &y, &z);
			re[y].push_back(make_pair(x, z));
		}
		for (int i = 0; i < 2; i++) {
			for (int j = 0; j <= n; j++)
				for (int k = 0; k <= n; k++)
					for (int t = 0; t <= n; t++)
						dp[i][j][k][t] = 0;
		}
		for (int j = 0; j <= n; j++)
			for (int k = 0; k <= j; k++)
				for (int t = 0; t <= k; t++)
					dp[0][j][k][t] = 0;
		dp[0][0][0][0] = 1;
		for (int i = 1; i <= n; i++) {
			for (int j = 0; j <= i; j++)
				for (int k = 0; k <= j; k++)
					for (int t = 0; t <= k; t++)
						dp[i & 1][j][k][t] = 0;
			for (int j = 0; j <= i; j++)
				for (int k = 0; k <= j; k++)
					for (int t = 0; t <= k; t++) {
						int p = (i & 1) ^ 1;
						dp[i & 1][j][k][t] = (dp[i & 1][j][k][t] + dp[p][j][k][t]) % mod;
						dp[i & 1][i - 1][k][t] = (dp[i & 1][i - 1][k][t] + dp[p][j][k][t]) % mod;
						dp[i & 1][i - 1][j][t] = (dp[i & 1][i - 1][j][t] + dp[p][j][k][t]) % mod;
						dp[i & 1][i - 1][j][k] = (dp[i & 1][i - 1][j][k] + dp[p][j][k][t]) % mod;
					}
			for (int j = 0; j <= i; j++)
				for (int k = 0; k <= j; k++)
					for (int t = 0; t <= k; t++) {
						for (int t1 = 0; t1 < re[i].size(); t1++) {
							if(1 + (j >= re[i][t1].first) + (k >= re[i][t1].first) + (t >= re[i][t1].first) != re[i][t1].second) {
								dp[i & 1][j][k][t] = 0;
							}
						}
					}
		}
		int ans = 0;
		for (int i = 0; i <= n; i++)
			for (int j = 0; j <= i; j++)
				for (int k = 0; k <= j; k++)
					ans = (ans + dp[n & 1][i][j][k]) % mod;
		printf("%d\n", ans);
	}
}

  

原文地址:https://www.cnblogs.com/pkgunboat/p/11241228.html

时间: 2024-10-09 04:54:55

2019HDU多校第一场 BLANK DP的相关文章

[2019HDU多校第一场][HDU 6590][M. Code]

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6590 题目大意(来自队友):二维平面上有\(n\)个点,每个点要么是黑色要么是白色,问能否找到一条直线将平面分割成黑白两部分 题解:分别对每种颜色的点求凸包,判断是否相交即可. (有模板真好) 1 #include<bits/stdc++.h> 2 //#include<cstdio> 3 //#include<cmath> 4 //#include<algorith

2019HDU多校第一场 6582 Path 【最短路+最大流最小割】

一.题目 Path 二.分析 首先肯定要求最短路,然后如何确定所有的最短路其实有多种方法. 1 根据最短路,那么最短路上的边肯定是可以满足$dist[from] + e.cost = dist[to]$.所以可以求一遍后根据这个公式再向网络图中的加边即可. 2 可以从源点和汇点分别求最短路,然后根据每条边肯定满足$dist1[e.from] + e.cost + dist2[e.to] = dij$,再加边就行了. 确定最短路后,由于是求最小割,直接跑$Dinic$求出最大流就可以了. 三.AC

2014多校第一场J题 || HDU 4870 Rating(DP || 高斯消元)

题目链接 题意 :小女孩注册了两个比赛的帐号,初始分值都为0,每做一次比赛如果排名在前两百名,rating涨50,否则降100,告诉你她每次比赛在前两百名的概率p,如果她每次做题都用两个账号中分数低的那个去做,问她最终有一个账号达到1000分需要做的比赛的次数的期望值. 思路 :可以直接用公式推出来用DP做,也可以列出210个方程组用高斯消元去做. (1)DP1:离散化.因为50,100,1000都是50的倍数,所以就看作1,2,20.这样做起来比较方便. 定义dp[i]为从 i 分数到达i+1

2014多校第一场 E 题 || HDU 4865 Peter&#39;s Hobby (DP)

题目链接 题意 : 给你两个表格,第一个表格是三种天气下出现四种湿度的可能性.第二个表格是,昨天出现的三种天气下,今天出现三种天气的可能性.然后给你这几天的湿度,告诉你第一天出现三种天气的可能性,让你求出最可能出现的天气序列 . 思路 : 定义第 i 天叶子湿度为hum[i].第 i 天,天气为 j 的最大概率为dp[i][j].wealea[i][j]表示天气为 i 叶子为j的概率,weawea[i][j]表示今天天气为 i 明天天气为j的概率,st[i]表示第一天天气为i的概率.pre[i]

2014多校第一场A题 || HDU 4861 Couple doubi

题目链接 题意 : 有K个球,给你一个数P,可以求出K个值,(i=1,2,...,k) : 1^i+2^i+...+(p-1)^i (mod p).然后女朋友先取,再xp取,都希望赢,如果女朋友能赢输出YES,否则输出NO 思路 :这个题,在纸上算算差不多就出来结果了,因为要赢,所以一开始必定拿大的,根据规律可以发现最后的那个取余结果不是0就是某个数,所以就看那个数有奇数个还是偶数个即可. 官方题解: 1 #include <stdio.h> 2 #include <string.h&g

2014多校第一场 I 题 || HDU 4869 Turn the pokers(费马小定理+快速幂模)

题目链接 题意 : m张牌,可以翻n次,每次翻xi张牌,问最后能得到多少种形态. 思路 :0定义为反面,1定义为正面,(一开始都是反), 对于每次翻牌操作,我们定义两个边界lb,rb,代表每次中1最少时最少的个数,rb代表1最多时的个数.一张牌翻两次和两张牌翻一次 得到的奇偶性相同,所以结果中lb和最多的rb的奇偶性相同.如果找到了lb和rb,那么,介于这两个数之间且与这两个数奇偶性相同的数均可取到,然后在这个区间内求组合数相加(若lb=3,rb=7,则3,5,7这些情况都能取到,也就是说最后的

暑期多校 第一场

A: 描述: 代码: 1 #include <iostream> 2 #include <stdio.h> 3 #include <math.h> 4 #include <string.h> 5 using namespace std; 6 7 int k,p; 8 int a[104]; 9 //void solve(){ 10 // int ans=0; 11 // for(int i=1;i<=k;i++){ 12 // ans=0; 13 //

2014多校第一场D题 || HDU 4864 Task (贪心)

题目链接 题意 : 用N台机器,M个任务,每台机器都有一个最大工作时间和等级,每个任务有一个需要工作时间和一个等级.如果机器完成一个任务要求是:机器的工作时间要大于等于任务的时间,机器的等级要大于等于任务的等级.一台机器只能完成一个任务,一个任务只能被一台机器完成.每个机器完成一个任务公司能够获得500*xi+2*yi (此处xy都是指被完成的任务的).输出所有机器能完成的最多任务数,和最大盈利. 思路 :贪心,自己做的时候想了各种排序都不对,没有考虑到500*xi+2*yi 这个公式的重要性.

多校第一场 费马小定理+模拟+组合数学

A题:Couple doubi 链接:http://acm.hdu.edu.cn/showproblem.php?pid=4861 这题逗逼了,刚开始根本就没什么思路,刚开始看题的时候有点像费马小定理,但是这个定理我只知道,然后没用过.看了下定义,有点不一样的是反着的,然后反着的我又不会转化,尼玛,就这样错过了最好的解题方法.然后队友又理解错题意了.WA了多发,然后我重新看了下题意,然后队友才发觉理解错题意了,然后找了规律才A. 代码比较短,就不贴了,真的写吧. if(k/(p-1)&1) pu