Array Without Local Maximums(還原陣列,多個區間加操作的化簡)
阿新 • • 發佈:2019-01-22
題意:
有一個數組符號,每一項有:,現在有一些位置變成了-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);
}