hdu 6578 Blank dp求 给定区间中数字不同的方案数

62 篇文章 0 订阅

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6578

 

题意:

       给你n个空的数列位置和k条限制,要你用0 1 2 3四个数字,每条限制由l r x组成, 表示你构成的数列在区间l到r的范围内至少有x个不同的数字,问你有多少数组的构成方式。

做法:

       我们能发现的是,其实区间内的数的表示只和数的最后出现位置有关系,但可能有人会说123和321肯定是不一样的方式啊,是的,但是这在转移的过程中会有顺序的出现,最后肯定都是会归到答案里的。一会儿再举例子。

       先说dp方程,dp[i][j][k][z] 表示四个数字在排序之后(即已经确定i>=j>=k>=z)的最后一个出现位置为i,j,k,z的方案数,因为i是最大的数字,那么我们只要看r是i的那些限制条件是否满足就可以,即i j k z大于等于L的数量是不是等于x即可,如果可以,那么我们就将当前位置i j k z加到下一个状态,如把当前在j位置的数字加到i+1,那么就是加到i+1 i k z 四个位置(因为j已经变成了i+1)。

        再来说例子,假设我们要在三个空白的位置上放上3个不同的数,那么一开始肯定都是从0 0 0 0开始的,转移到1 0 0 0的时候会有四次,即将第1 2 3 4    四个位置上的0变成1的过程,但这都是合法的所以最后1 0 0 0 状态会等于4(这也就代表第1个位置可以放上四个数),因为最后合法的肯定是3 2 1 0 所以我就直接看转移到 2 1 0 0 的数了, 2 1 0 0肯定是从1 0 0 0的后三个0的任意位置转移过来,即 (2 1 0 0) = 3*(1 0 0 0)= 12 ,最后的 (3 2 1 0)同理肯定是从(2 1 0 0)来的,而有两个来的方式,那么就是(3 2 1 0)=2 * (2 1 0 0)=24。例子不大手模一下也快的答案也就是24.

 


#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn=105;
const int mod=998244353;
ll dp[2][maxn][maxn][maxn],ans;
int n,m;
vector<pii> L[maxn];
void add(ll &a,ll b){
    a=a+b;
    if(a>mod) a-=mod;
}
inline bool ck(int a,int b,int c,int d){
    for(int i=0;i<L[a].size();i++){
        pii x=L[a][i];
        int ll=x.first;
        int num=1+(b>=ll)+(c>=ll)+(d>=ll);
        if(num!=x.second) return 0;
    }
    return 1;
}
int main(){
    int T;scanf("%d",&T);
    while(T--){
        memset(dp,0,sizeof(dp));
        scanf("%d%d",&n,&m);
        rep(i,1,n) L[i].clear();
        rep(i,1,m){
            int l,r,nu;
            scanf("%d%d%d",&l,&r,&nu);
            L[r].push_back({l,nu});
        }
        int p=1;
        dp[p][0][0][0]=1;
        ans=0;
        rep(i,1,n){
            p^=1;
            rep(j,0,i)
                rep(k,0,j)
                    rep(z,0,k)
                        dp[p][j][k][z]=0;

            rep(j,0,i)
                rep(k,0,j)
                    rep(z,0,k){
                        if(ck(i,j,k,z)) add(dp[p][j][k][z],dp[p^1][j][k][z]);
                        //将原来在i位置上的数字加到i+1位置上 那么最大的发生变化而其他不变
                        if(ck(i,i-1,k,z)) add(dp[p][i-1][k][z],dp[p^1][j][k][z]);
                        if(ck(i,i-1,j,z)) add(dp[p][i-1][j][z],dp[p^1][j][k][z]);
                        if(ck(i,i-1,j,k)) add(dp[p][i-1][j][k],dp[p^1][j][k][z]);
                        //将原来在z位置上的数字加到i+1上 其他不变 z变为i+1
                    }
        }
        rep(i,0,100)
            rep(j,0,i)
                rep(k,0,j)
                    add(ans,dp[p][i][j][k]);
        printf("%lld\n",ans);
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值