1. 程式人生 > >Array Without Local Maximums(還原陣列,多個區間加操作的化簡)

Array Without Local Maximums(還原陣列,多個區間加操作的化簡)

題意:

有一個數組符號,每一項有:Max(a[i1],a[i+1])>a[i]Max(a[i-1],a[i+1])>a[i],現在有一些位置變成了-1(看不見),問還原陣列的方案數

解析:

因為資料大小1~200,所以可以暴力dp,因為每個位置都要判斷與前後的關係比較麻煩,所以轉移成:前一位置已符合要求 或 未符合要求。

dp[f][i][1]表示在當前位,值位i,已符合要求的方案數,如果已符合要求,那麼如果下面一位比它小或等於,則轉移為dp[!f][<=i][1],大於等於則:dp[!f][>i][0]
dp[f][i][0]

表示未符合,說明下一位必須大於等於這一位,轉移到:dp[!f][=i][1]dp[!f][>i][0]

這裡需要用到一個小技巧:

dp[f][i][1]dp[f][i][0]會轉移到一個區間,例如>i,難道我們for一遍加上去嗎?這樣會多出一個200的時間複雜度

處理技巧為:

假設此次操作為[a,b]加上v,那麼我們開一個數組,在a的地方+v,在b+1的地方-v,最後只需要在多次處理完後,for一遍這個陣列,即可得出所有區間操作後的陣列

取模技巧:

dp的維護可以看到,是200次加法操作後再取模而不是每次都取模,因為取模操作太慢,200*mod又不會爆long long,事實證明減少了一半不止的時間

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL dp[2][201][2];int f=1;
int a[100009];
const LL mod=998244353;

int main(){
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",a+i);
    }

    if(a[1]==-1){
        for(int i=1;i<=200;i++)dp[f][i][1]=
1; } else dp[f][a[1]][1]=1; for(int i=2;i<=n;i++){ if(a[i]!=-1){ int u=a[i]; for(int j=1;j<u;j++)dp[!f][u][1]+=dp[f][j][1]+dp[f][j][0]; dp[!f][u][0]+=dp[f][u][1]; for(int j=u;j<=200;j++)dp[!f][u][0]+=dp[f][j][0]; dp[!f][u][0]%=mod; dp[!f][u][1]%=mod; memset(dp[f],0,sizeof(dp[f]));f=!f; } else{ LL tmp[2][301];memset(tmp,0,sizeof(tmp));//區間操作化簡 for(int j=1;j<=200;j++){ tmp[0][j]+=dp[f][j][1]; tmp[0][j+1]-=dp[f][j][1]; tmp[1][j+1]+=dp[f][j][1]; tmp[0][1]+=dp[f][j][0]; tmp[0][j+1]-=dp[f][j][0]; tmp[1][j+1]+=dp[f][j][0]; } LL now0=0,now1=0; for(int j=1;j<=200;j++){ now0+=tmp[0][j]; now1+=tmp[1][j]; now0%=mod,now1%=mod; dp[!f][j][0]=now0; dp[!f][j][1]=now1; } memset(dp[f],0,sizeof(dp[f]));f=!f; } } LL ans=0; for(int i=1;i<=200;i++){ ans+=dp[f][i][0]; } ans=(ans%mod+mod)%mod; printf("%I64d\n",ans); }