Blank(HDU-6578)

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 3

Sample 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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值