2019 Multi-University Training Contest 1 1001.Blank

A.Blank

题意:给你长度为 n1<=n<=100n(1<=n<=100) 得数组,往里面填数 {0,1,2,3} 四个数字,有 m 个约束条件,第i个约束条件是:区间 [li,ri][l_{i},r_{i}]xix_{i} 种不同的数,问一共有多少种满足约束条件的序列。
题解:
在这里插入图片描述
分析:
我们可以用 dp(i,p1,p2,p3,p4) 表示第 i 个位置 {0,1,2,3}对应出现的最后一个位置,对于第 i 个约束条件,把不满足的 × 掉就行了,状态方程也很好写

dp(i1,p1p2p3p4)>{dp(iip2p3p4)0dp(ip1ip3p4)1dp(ip1p2ip4)2dp(ip1p2p3i)3dp( i-1, p1,p2,p3,p4) ->\left\{\begin{matrix} & dp(i,i,p2,p3,p4); 填0 \\& dp(i,p1,i,p3,p4); 填1 \\& dp(i,p1,p2,i,p4); 填2 \\&dp(i,p1,p2,p3,i); 填3 \end{matrix}\right.

但是存在一个致命的问题,上式时间复杂度 O(n5^{5}) ;滚动数组优化之后,空间复杂度为 O(n4^{4})
两个都不行,因此我们需要优化上述状态。
经过观察发现,{0,1,2,3} 的顺序对于约束条件并没什么作用,只是方便状态转移,并且对于第 i 个位置来讲,一定会存在该位置 i,因此我们可以将 {0,1,2,3} 最后一次出现的位置从小到达排序,设为 ( p1,p2,p3,i ); i 可以忽略掉不写,然后就是思考怎么进行状态转移了.
设第 i-1 个状态 p1,p2,p3,i-1 (p1 < p2 <p3 <i-1)
因为是{0,1,2,3}无序,我们直接考虑 第 i 位填的数字会对应替换掉哪一位
则有以下四个状态:
p2,p3,i-1,i
p1,p3,i-1,i
p1,p2,i-1,i
p1,p2,p3,i
注:因为 i 随着改变,不用在dp方程里写出来,我上面只是为了更加方便理解
则状态方程为:
dp(i1,p1p2p3)&gt;{dp(ip2p3i1)dp(ip1p3i1)dp(ip1p2i1)dp(ip1p2p3)dp( i-1, p1,p2,p3) -&gt;\left\{\begin{matrix} &amp; dp(i,p2,p3,i-1); \\&amp; dp(i,p1,p3,i-1); \\&amp; dp(i,p1,p2,i-1); \\&amp;dp(i,p1,p2 ,p3); \end{matrix}\right.
注:上述转移方程第 1 项的那个 i-1 和 i 只是代表他们是第几个状态,是要用滚动数组存的


现在是如何加上限制条件:区间 [li,ri][l_{i},r_{i}]xix_{i} 种不同的数
dp(i,p1,p2,p3)表示的是{0,1,2,3}出现的位置排序后为 p1,p2,p3,i
因此我们可以先将 限制条件按 右区间 从小到大排序,处理第 i 个位置的时候,如果 i == r[x],我们将O(n3^{3})枚举p1,p2,p3就行了,将不满足的置0.
时间复杂度:O(n4^{4}) , 空间复杂度为 O(n3^{3})

/*
卡常数,没写好就超时,心态爆炸 
900+ ms 
*/
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 100 + 5;
const int mod=998244353;
int dp[2][N][N][N];
vector<vector<pair<int,int> > >V(N);
void inline slove(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    	V[i].clear();
    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));
    }

    memset(dp,0,sizeof dp);
    dp[0][0][0][0]=1;
    for(int i=1;i<=n;i++)    //枚举第 i 位填什么
    {
        int t1=i&1,t2=(i-1)&1;
        for(int j=0;j<=i;j++)
            for(int k=0;k<=i;k++)
                for(int z=0;z<=i;z++)
                    dp[t1][j][k][z]=0;

        for(int p1=0;p1<i;p1++)
            for(int p2=p1;p2<=i;p2++)
                for(int p3=p2;p3<=i;p3++)
                {
                    dp[t1][p2][p3][i-1]=(dp[t1][p2][p3][i-1]+dp[t2][p1][p2][p3])%mod;
                    dp[t1][p1][p3][i-1]=(dp[t1][p1][p3][i-1]+dp[t2][p1][p2][p3])%mod;
                    dp[t1][p1][p2][i-1]=(dp[t1][p1][p2][i-1]+dp[t2][p1][p2][p3])%mod;
                    dp[t1][p1][p2][p3] =(dp[t1][p1][p2][p3] +dp[t2][p1][p2][p3])%mod;
                }
        //检查
        for(int p1=0;p1<i;p1++)
            for(int p2=p1;p2<i;p2++)
                for(int p3=p2;p3<i;p3++)
                    for(int z=0;z<V[i].size();z++)
                    {
                        int x= (i >=V[i][z].first);
                            x+=(p1>=V[i][z].first);
                            x+=(p2>=V[i][z].first);
                            x+=(p3>=V[i][z].first);
                        if(x!=V[i][z].second)
                            dp[t1][p1][p2][p3]=0;   //当前状态清0
                    }

    }
    LL ans=0;
    for(int p1=0;p1<=n;p1++)
        for(int p2=p1;p2<=n;p2++)
            for(int p3=p2;p3<=n;p3++)
                ans=ans+dp[n&1][p1][p2][p3];
    printf("%lld\n",ans%mod);

}
int main() {
    int t;
    scanf("%d",&t);
    while(t--)
    	slove();

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章