Description
给你 n n n 个格子,从左到右依次编号为 1 ∼ n 1∼n 1∼n。
请你给每个格子填入一个 0 ∼ 3 0∼3 0∼3 的整数,满足给定的若干条件,第 i i i 个条件的描述为:
在 l i ∼ r i l_i∼r_i li∼ri 的区间内,恰好有 x i x_i xi个互不相同的整数。
请你计算共有多少种满足条件的填写方法,将答案对 998244353 998244353 998244353 取模。
Format
Input
第一行一个整数 T ( 1 ≤ T ≤ 15 ) T(1\leq T\leq 15) T(1≤T≤15) 表示共有 T T T 组测试数据。
每组测试数据,第一行有两个整数 n ( 1 ≤ n ≤ 100 ) , m ( 0 ≤ m ≤ 100 ) n(1\leq n\leq 100),m(0\leq m\leq 100) n(1≤n≤100),m(0≤m≤100),分别表示空格子以及条件的数量。
接着连续 m m m 行,每行三个整数 l , r , x l,r,x l,r,x,表示需要满足的条件 ( 1 ≤ l ≤ r ≤ n , 1 ≤ x ≤ 4 ) (1\leq l\leq r\leq n,1\leq x\leq 4) (1≤l≤r≤n,1≤x≤4)。
Output
对于每一组测试数据,输出一行,一个整数,表示答案对 998244353 998244353 998244353 取模的结果。
Samples
输入数据 1
2
1 0
4 1
1 3 3
输出数据 1
4
96
Limitation
1s, 1024KiB for each test case.
简洁题解
dp题,开四维,表四色,最后位。
条件右端当下标,每次状态确定对。
我为人人四转移,滚动空间就完美
猜答案 (逃
这题,首先根据 n ≤ 100 n\leq 100 n≤100 且有 4 4 4 种颜色,我们可以推测,正解复杂度为 O ( n 4 ) O(n^4) O(n4)(玄学想法)
然后就可以反推出这是道动态规划题目,可以构造动态规划 d p [ i ] [ j ] [ k ] [ l ] dp[i][j][k][l] dp[i][j][k][l],其中 i , j , k , l i,j,k,l i,j,k,l为每种颜色的最后一位且 i < j < k < l i<j<k<l i<j<k<l
转移方程
现在要加一个位置,那就不难,“我为人人”,更新
l
+
1
l+1
l+1就行
至于剩下的位置,就有
C
4
3
=
4
C_4^3=4
C43=4种
我们可以轻松解决这4种
(
d
p
[
i
]
[
j
]
[
k
]
[
l
+
1
]
+
=
d
p
[
i
]
[
j
]
[
k
]
[
l
]
)
(dp[i][j][k][l+1]+=dp[i][j][k][l])%=mod
(dp[i][j][k][l+1]+=dp[i][j][k][l])
(
d
p
[
i
]
[
j
]
[
l
]
[
l
+
1
]
+
=
d
p
[
i
]
[
j
]
[
k
]
[
l
]
)
(dp[i][j][l][l+1]+=dp[i][j][k][l])%=mod
(dp[i][j][l][l+1]+=dp[i][j][k][l])
(
d
p
[
i
]
[
k
]
[
l
]
[
l
+
1
]
+
=
d
p
[
i
]
[
j
]
[
k
]
[
l
]
)
(dp[i][k][l][l+1]+=dp[i][j][k][l])%=mod
(dp[i][k][l][l+1]+=dp[i][j][k][l])
(
d
p
[
j
]
[
k
]
[
l
]
[
l
+
1
]
+
=
d
p
[
i
]
[
j
]
[
k
]
[
l
]
)
(dp[j][k][l][l+1]+=dp[i][j][k][l])%=mod
(dp[j][k][l][l+1]+=dp[i][j][k][l])
满足条件
按照右端点当作下标,装进一个
v
e
c
t
o
r
vector
vector去维护
每次到一个状态按右端点取出进行判定是否满足条件即可
不符合直接判为
0
0
0,对后边就没影响了
完美……吗?
滚动数组
很明显……空间不能
O
(
n
4
)
O(n^4)
O(n4)
我们必须滚动,毕竟开支不能这么大(128MB,炸了)
那么滚动那一层?
第一层、第二层、第三层都存在不可控变动(KaTeX parse error: Expected 'EOF', got '&' at position 2: i&̲1 can = j&1, j&…),故滚动第四层
注意:第四层的循环得提前到第一位。
代码
#pragma GCC optimize(1, "inline", "Ofast")
#pragma GCC optimize(2, "inline", "Ofast")
#pragma GCC optimize(3, "inline", "Ofast")
// #include <bits/extc++.h>
#include <bits/stdc++.h>
#define memery() std::cerr << "The memory is " << (&ed - &st) / 1024.0 / 1024 << "MB." << std::endl
//#include <"C:\Users\admin\Documents\myhead\fstio.hpp">
//#include <"C:\Users\admin\Documents\myhead\def.hpp">
//#include <"C:\Users\admin\Documents\myhead\help.h">
// using namespace __gnu_cxx;
// using namespace __gnu_pbds;
using namespace std;
//变量区
bool st;
const int N = 100 + 7, md = 998244353;
//please write sth.
int t, n, m;
vector<pair<int, int>> vt[N];
int l, r, x;
int a[5];
long long dp[N][N][N][2];
bool ed;
//函数区
void input()
{
//please write sth.
cin >> n >> m;
for (int i = 1; i <= n; i++)
vt[i].clear();
for (int i = 1; i <= m; i++)
cin >> l >> r >> x, vt[r].push_back(make_pair(l, x));
}
int plu(int x) { return (x == 0) ? x : x + 1; }
int fplu(int x) { return (x == 0) ? x : x - 1; }
void work()
{
memset(dp, 0, sizeof dp);
dp[0][0][0][0] = 1;
for (a[4] = 0; a[4] <= n; a[4]++) {
for (a[3] = 0; a[3] <= fplu(a[4] + 1); a[3]++)
for (a[2] = 0; a[2] <= fplu(a[3]); a[2]++)
for (a[1] = 0; a[1] <= fplu(a[2]); a[1]++)
dp[a[1]][a[2]][a[3]][(a[4] + 1) & 1] = 0;//清空无用数组
for (a[3] = 0; a[3] <= fplu(a[4]); a[3]++)
for (a[2] = 0; a[2] <= fplu(a[3]); a[2]++)
for (a[1] = 0; a[1] <= fplu(a[2]); a[1]++) {
for (auto i : vt[a[4]])
if (a[4 - i.second] >= i.first || ( a[5 - i.second] < i.first))
dp[a[1]][a[2]][a[3]][a[4] & 1] = 0;//判定是否符合条件
(dp[a[1]][a[2]][a[3]][(a[4] + 1) & 1] += dp[a[1]][a[2]][a[3]][a[4] & 1]) %= md;
(dp[a[1]][a[2]][a[4]][(a[4] + 1) & 1] += dp[a[1]][a[2]][a[3]][a[4] & 1]) %= md;
(dp[a[1]][a[3]][a[4]][(a[4] + 1) & 1] += dp[a[1]][a[2]][a[3]][a[4] & 1]) %= md;
(dp[a[2]][a[3]][a[4]][(a[4] + 1) & 1] += dp[a[1]][a[2]][a[3]][a[4] & 1]) %= md;
}//转移方程
}
}
void output()
{
//please write sth.
long long ans = 0;
for (int i = 0; i < n; i++)
for (int j = plu(i); j < n; j++)
for (int k = plu(j); k < n; k++)
(ans += dp[i][j][k][n & 1]) %= md;
cout << ans << endl;
}
//主函数
int main()
{
//memory();
std::ios::sync_with_stdio(0);
std::cin.tie(), std::cout.tie();
cin >> t;
while (t--) {
input();
work();
output();
}
}
/*
定义 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(n^4 )。
滚动一维,空间复杂度 O(n^3 )
*/