Problem Description
There are N blanks arranged in a row. The blanks are numbered 1,2,…,N from left to right.
Tom is filling each blank with one number in {0,1,2,3}. According to his thought, the following M conditions must all be satisfied. The ith condition is:
There are exactly xi different numbers among blanks ∈[li,ri].
In how many ways can the blanks be filled to satisfy all the conditions? Find the answer modulo 998244353.Input
The first line of the input contains an integer T(1≤T≤15), denoting the number of test cases.
In each test case, there are two integers n(1≤n≤100) and m(0≤m≤100) in the first line, denoting the number of blanks and the number of conditions.
For the following m lines, each line contains three integers l,r and x, denoting a condition(1≤l≤r≤n, 1≤x≤4).Output
For each testcase, output a single line containing an integer, denoting the number of ways to paint the blanks satisfying all the conditions modulo 998244353.
Sample Input
2
1 0
4 1
1 3 3Sample Output
4
96
题意:给出一个长度为 n 的数组,每个位置要填入 0~3 中的任意一个数,再给出 m 个限制条件,l r x 代表区间 [l,r] 中出现的数字种数恰好为 x 种,问有多少种方案
思路:
限制是一个区间内有几个不同的数,因此不必知道每个位置具体放的是什么数,定义一个四维数组 dp[i][j][k][p][cur] 来存储数字 0、1、2、3 出现的最后位置为 i、j、k、p ,用 cur 来表示填充到第 cur 个格子
那么根据以求得的 dp[i][j][k][p][cur-1] 可以得到状态转移方程:
- 下一位填 0:dp[cur][j][k][t][cur] = dp[cur][j][k][t][cur] + dp[i][j][k][t][cur−1]
- 下一位填 1:dp[i][cur][k][t][cur] = dp[i][cur][k][t][cur] + dp[i][j][k][t][cur−1]
- 下一位填 2:dp[i][j][cur][t][cur] = dp[i][j][cur][t][cur] + dp[i][j][k][t][cur−1]
- 下一位填 3:dp[i][j][k][cur][cur] = dp[i][j][k][cur][cur] + dp[i][j][k][t][cur−1]
这样对于每一个状态转移方程,当 cur=r 时,根据 0、1、2、3 出现的最后位置 i、j、k、p 来判断是否大于等于 l,从而得出有几个不同的数,如果不同的数的个数不等于 x,那么说明不满足限制条件,置 dp[i][j][k][p][cur]=0
此时时间复杂度、空间复杂度都达到 O(n^5),超出题目给定的范围,需要进行优化
由于 i、j、k、p 表示 0、1、2、3 最后一次出现的位置,那么可以发现,其中一定有一个值是等于 cur 的,而且互不相等,那么可将 5 维数组 dp[i][j][k][p][cur] 压缩到 4 维 dp[i][j][k][cur] 以代表 i、j、k、cur 是不同的数最后出现的位置,且 i<j<k<cur
于是,根据以求得的 dp[i][j][k][cur-1] 可以得到状态转移方程:
- 下一位填 0:dp[j][k][cur−1][cur] = dp[j][k][cur−1][cur] + dp[i][j][k][cur−1]
- 下一位填 1:dp[j][k][cur−1][cur] = dp[j][k][cur−1][cur] + dp[i][j][k][cur−1]
- 下一位填 2:dp[i][j][cur−1][cur] = dp[i][j][cur−1][cur] + dp[i][j][k][cur−1]
- 下一位填 3:dp[i][j][cur−1][cur] = dp[i][j][cur−1][cur] + dp[i][j][k][cur−1]
这样一来,时间复杂度、空间复杂度都优化到 O(n^4),时间可以,但空间仍然超出范围
根据动态规划的无后效性原则,dp[i][j][k][cur] 只与 dp[i][j][k][cur-1] 有关,那么可以利用滚动数组来再次优化
设 dp[i][j][k][2],那么就有状态转移方程:
- dp[j][k][cur−1][now] = dp[j][k][cur−1][now] + dp[i][j][k][pre]
- dp[i][k][cur−1][now] = dp[i][k][cur−1][now] + dp[i][j][k][pre]
- dp[i][j][cur−1][now] = dp[i][j][cur−1][now] + dp[i][j][k][pre]
- dp[i][j][k][now] = dp[i][j][k][now] + dp[i][j][k][pre]
由于是累加,因此在利用滚动数组时,dp[i][j][k][now] 每次用完要进行清空
这样时间复杂度为 O(n^4),空间复杂度为 O(2*n^3),满足题目限制
Source Program
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<unordered_map>
#include<bitset>
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<int,int>
LL quickPow(LL a,LL b){ LL res=1; while(b){if(b&1)res*=a; a*=a; b>>=1;} return res; }
LL quickModPow(LL a,LL b,LL mod){ LL res=1; a=a%mod; while(b){if(b&1)res=(a*res)%mod; a=(a*a)%mod; b>>=1;} return res; }
LL getInv(LL a,LL mod){ return quickModPow(a,mod-2,mod); }
LL GCD(LL x,LL y){ return !y?x:GCD(y,x%y); }
LL LCM(LL x,LL y){ return x/GCD(x,y)*y; }
const double EPS = 1E-10;
const int MOD = 998244353;
const int N = 100+5;
const int dx[] = {-1,1,0,0,1,-1,1,1};
const int dy[] = {0,0,-1,1,-1,1,-1,1};
using namespace std;
int dp[N][N][N][2];
vector<Pair> V[N];
int main() {
int t;
scanf("%d",&t);
while(t--) {
memset(dp,0,sizeof(dp));
for(int i=0; i<N; i++)
V[i].clear();
int n,m;
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++) {
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
V[r].push_back(make_pair(l,x));
}
dp[0][0][0][0]=1;
for(int cur=1; cur<=n; cur++) {
int now=cur%2?1:0;
int pre=!now;
//清空dp[i][j][k][now]
for(int i=0; i<=cur; i++)
for(int j=i; j<=cur; j++)
for(int k=j; k<=cur; k++)
dp[i][j][k][now]=0;
//状态转移
for(int i=0; i<=cur; i++) {
for(int j=i; j<=cur; j++) {
for(int k=j; k<=cur; k++) {
dp[i][j][k][now] += dp[i][j][k][pre];
dp[j][k][cur-1][now] += dp[i][j][k][pre];
dp[i][k][cur-1][now] += dp[i][j][k][pre];
dp[i][j][cur-1][now] += dp[i][j][k][pre];
//取模
dp[i][j][k][now] %= MOD;
dp[j][k][cur-1][now] %= MOD;
dp[i][k][cur-1][now] %= MOD;
dp[i][j][cur-1][now] %= MOD;
}
}
}
for(int i=0; i<=cur; i++) {
for(int j=i; j<=cur; j++) {
for(int k=j; k<=cur; k++) {
for(int p=0; p<V[cur].size(); p++) {
int l=V[cur][p].first;
int r=cur;
int x=V[cur][p].second;
if(((i>=l)+(j>=l)+(k>=l)+(cur>=l)) != x)//[l,r]中数字个数不为x
dp[i][j][k][now]=0;
}
}
}
}
}
int res=0;
for(int i=0; i<=n; i++)
for(int j=i; j<=n; j++)
for(int k=j; k<=n; k++)
res=(res+dp[i][j][k][n%2?1:0])%MOD;
printf("%d\n",res);
}
return 0;
}