生活随笔
收集整理的這篇文章主要介紹了
兰州大学第一届 飞马杯 ★★飞马祝福语★★ 线段树维护dp(动态dp)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
傳送門
文章目錄
題意:
給你一個串,每次將區間都修改為某一個字母,問最終包含多少個FeiMaFeiMaFeiMa子序列。
思路:
首先暴力修改肯定是不行的,復雜度nqnqnq。
如果沒有修改操作,有一個顯然的dpdpdp方程,dp[i][j]dp[i][j]dp[i][j]表示到了第iii個,jjj對應FeiMaFeiMaFeiMa某個子序列的數量,考慮用線段樹優化這個dpdpdp。
定義dp[u][l][r]dp[u][l][r]dp[u][l][r]表示到了線段樹第uuu個子樹包含FeiMaFeiMaFeiMa的[l,r][l,r][l,r]序列的個數,答案即為dp[1][0][4]dp[1][0][4]dp[1][0][4]。轉移方程也比較明顯了:dp[u][l][r]=dp[u<<1][l][r]+dp[u<<1∣1][l][r]+∑k=lr?1(dp[u<<1][l][k]+dp[u<<1∣1][k+1][r])dp[u][l][r]=dp[u<<1][l][r]+dp[u<<1|1][l][r]+\sum _{k=l} ^{r-1}(dp[u<<1][l][k]+dp[u<<1|1][k+1][r])dp[u][l][r]=dp[u<<1][l][r]+dp[u<<1∣1][l][r]+k=l∑r?1?(dp[u<<1][l][k]+dp[u<<1∣1][k+1][r])
這個直接在pushuppushuppushup中維護即可。
對于區間修改,我們維護一個懶標記即可,修改的時候(如果是FeiMaFeiMaFeiMa的某個字母的話)直接讓dp[u][x][x]=Len(u)dp[u][x][x]=Len(u)dp[u][x][x]=Len(u)即可。
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid ((tr[u].l+tr[u].r)>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std
;
typedef long long LL
;
typedef unsigned long long ULL
;
typedef pair
<int,int> PII
;const int N
=100010,mod
=998244353,INF
=0x3f3f3f3f;
const double eps
=1e-6;int n
,q
;
char s
[N
];
string has
="FeiMa";
struct Node {int l
,r
;int lazy
;LL dp
[5][5];
}tr
[N
<<2];int get(char ch
) {if(ch
=='F') return 0;else if(ch
=='e') return 1;else if(ch
=='i') return 2;else if(ch
=='M') return 3;else if(ch
=='a') return 4;else return 5;
}void calc(int u
,int x
) {for(int i
=0;i
<5;i
++) for(int j
=0;j
<5;j
++) tr
[u
].dp
[i
][j
]=0;if(x
<5) tr
[u
].dp
[x
][x
]=(tr
[u
].r
-tr
[u
].l
+1)%mod
;
}void pushup(int u
) {for(int l
=0;l
<5;l
++) {for(int r
=l
;r
<5;r
++) {tr
[u
].dp
[l
][r
]=(tr
[L
].dp
[l
][r
]+tr
[R
].dp
[l
][r
])%mod
;for(int k
=l
;k
<=r
-1;k
++) tr
[u
].dp
[l
][r
]+=tr
[L
].dp
[l
][k
]*tr
[R
].dp
[k
+1][r
]%mod
,tr
[u
].dp
[l
][r
]%=mod
;}}
}void pushdown(int u
) {if(tr
[u
].lazy
==-1) return;tr
[L
].lazy
=tr
[R
].lazy
=tr
[u
].lazy
;calc(L
,tr
[u
].lazy
); calc(R
,tr
[u
].lazy
);tr
[u
].lazy
=-1;
}void build(int u
,int l
,int r
) {tr
[u
]={l
,r
,-1};if(l
==r
) {calc(u
,get(s
[l
]));return;}build(L
,l
,Mid
); build(R
,Mid
+1,r
);pushup(u
);
}void change(int u
,int l
,int r
,int x
) {if(tr
[u
].l
>=l
&&tr
[u
].r
<=r
) {tr
[u
].lazy
=x
;calc(u
,x
);return;}pushdown(u
);if(l
<=Mid
) change(L
,l
,r
,x
);if(r
>Mid
) change(R
,l
,r
,x
);pushup(u
);
}int main()
{
int _
; scanf("%d",&_
);while(_
--) {scanf("%d%d%s",&n
,&q
,s
+1);build(1,1,n
);while(q
--) {int l
,r
;char ch
;scanf("%d%d %c",&l
,&r
,&ch
);change(1,l
,r
,get(ch
));printf("%lld\n",tr
[1].dp
[0][4]%mod
);}}return 0;
}
總結
以上是生活随笔為你收集整理的兰州大学第一届 飞马杯 ★★飞马祝福语★★ 线段树维护dp(动态dp)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。