填空 题解

该篇文章详细介绍了使用动态规划解决一个问题,即在给定格子中以特定规则填入整数,计算满足条件的不同填法数量,最终答案对998244353取模。
摘要由CSDN通过智能技术生成

谨慎进入

Description

给你 n n n 个格子,从左到右依次编号为 1 ∼ n 1∼n 1n

请你给每个格子填入一个 0 ∼ 3 0∼3 03 的整数,满足给定的若干条件,第 i i i 个条件的描述为:

l i ∼ r i l_i∼r_i liri 的区间内,恰好有 x i x_i xi个互不相同的整数。

请你计算共有多少种满足条件的填写方法,将答案对 998244353 998244353 998244353 取模。

Format

Input

第一行一个整数 T ( 1 ≤ T ≤ 15 ) T(1\leq T\leq 15) T(1T15) 表示共有 T T T 组测试数据。

每组测试数据,第一行有两个整数 n ( 1 ≤ n ≤ 100 ) , m ( 0 ≤ m ≤ 100 ) n(1\leq n\leq 100),m(0\leq m\leq 100) n(1n100),m(0m100),分别表示空格子以及条件的数量。

接着连续 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) (1lrn,1x4)

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 n100 且有 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 ) 
*/
  • 27
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值