HDU - 6578 Blank DP + 滚动数组

HDU - 6578 Blank

题意

给你\(\{0,1,2, 3\}\)四个数,分别填入长度为\(n\)的数列中,有\(m\)个限制条件,\(l_{i}, r_{i}, x_{i}\)表示在\([l_{i}, r_{i}]\)区间内,只能有\(x_{i}\)个不同的数。问一共有多少总方案。

思路

首先, 我们可以用\(dp[i][j][k][w]\)来表示方案数,\(i, j, k, w\)不是特指对应某个数字,而是四种不同的数字从小到大最后出现的位置\((i < j < k <w)\)
那么我们就能推出\(dp\)方程

\[dp[i][j][k][w] += dp[i][j][k][w-1]$$ $$dp[i][j][w-1][w] += dp[i][j][k][w-1]$$ $$dp[i][k][w-1][w] += dp[i][j][k][w-1]$$ $$dp[j][k][w-1][w] += dp[i][j][k][w-1] \]

我们发现后一位都只与\(w-1\)有关,\(100^{4}\)的空间太大,所以我们要做一个优化,把最后一维改成大小为\(2\)的滚动数组
那么我们现在的\(dp\)方程为

\[dp[i][j][k][now] += dp[i][j][k][pre]$$ $$dp[i][j][pre][now] += dp[i][j][k][pre]$$ $$dp[i][k][pre][now] += dp[i][j][k][pre]$$ $$dp[j][k][pre][now] += dp[i][j][k][pre]$$ $$now = (pre+ 1)\%2 = pre \oplus1 \]

转移\(dp\)的时候考虑一下限制条件就\(OK\)

AC代码

#include <map>
#include <set>
#include <list>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#include <cfloat>
#include <string>
#include <vector>
#include <cstdio>
#include <bitset>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define  lowbit(x)  x & (-x)
#define  mes(a, b)  memset(a, b, sizeof a)
#define  fi         first
#define  se         second
#define  pii        pair<int, int>

typedef unsigned long long int ull;
typedef long long int ll;
const int    maxn = 1e2 + 10;
const int    maxm = 1e5 + 10;
const ll     mod  = 998244353;
const ll     INF  = 1e18 + 100;
const int    inf  = 0x3f3f3f3f;
const double pi   = acos(-1.0);
const double eps  = 1e-8;
using namespace std;

int n, m;
int cas, tol, T;
ll dp[maxn][maxn][maxn][3];
vector< pair<int,int> > lim[maxn];
int check(int a, int b, int c, int d){  //a < b < c < d
	for(int i = 0; i < lim[d].size(); i++){
		int x = lim[d][i].se;
		int l = lim[d][i].fi;
		if(x == 1 && l <= c)  
			return 0;
		if(x == 2 && (l <= b || l > c)) 
			return 0;
		if(x == 3 && (l <= a || l > b))
			return 0;
		if(x == 4 && (l > a))
			return 0;
	}
	return 1;
}

int main() {
	scanf("%d", &T);
	while(T--){
 		scanf("%d%d", &n, &m);
		int l, r, x;
		for(int i = 1; i <= n; i++){
			lim[i].clear();
		}
		for(int i = 1; i <= m; i++){
			scanf("%d%d%d", &l, &r, &x);
			lim[r].push_back(make_pair(l, x));
		}

		dp[0][0][0][0] = 1;
		int  now = 1;
		ll ans = 0;
		for(int w = 1; w <= n; w++){
            for(int k = 0;  k <= w;k++){    //每次初始化,据说memset会T,要手动初始化
                for(int j = 0; j <= k; j++){
                    for(int i = 0; i <= j; i++){
                       dp[i][j][k][now] = 0;
                    }
                }
            }
			for(int k = 0; k < w; k++){
				for(int j = 0; (!k && j <= k) || j < k; j++){			//(!k&&j <=k):比自己后一位为0的时候,本身也能等于0,反之小于后一位
					for(int i = 0; (!j && i <= j) || i < j; i++){
                        if(!check(i, j, k, w-1)){      //不符合限制条件的,令dp = 0
							dp[i][j][k][now^1] = 0;
							continue;
						}
						dp[i][j][k][now] = (dp[i][j][k][now] + dp[i][j][k][now^1])%mod;
						dp[i][j][w-1][now] = (dp[i][j][w-1][now] + dp[i][j][k][now^1])%mod;
						dp[i][k][w-1][now] = (dp[i][k][w-1][now] + dp[i][j][k][now^1])%mod;
						dp[j][k][w-1][now] = (dp[j][k][w-1][now] + dp[i][j][k][now^1])%mod;
					}
				}
			}
			now ^= 1;
		}
		now ^= 1;   //这边不要漏了
		for(int k = 0;  k < n;k++){
			for(int j = 0; (!k && j <= k) || j < k; j++){   
				for(int i = 0; (!j && i <= j) || i < j; i++){
					if(check(i, j, k, n))   //这边要判断一下是否符合限制条件
                        ans = (ans + dp[i][j][k][now])%mod;
				}
			}
		}
		printf("%lld\n", ans);
	}
	return 0;
}

posted @ 2019-07-31 01:23  竹攸  阅读(247)  评论(0编辑  收藏  举报