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();

}

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