【DP优化技巧】3. 线段树上(广义)矩阵乘法
例题
P9192 [USACO23OPEN] Pareidolia P
看见这种改 1 个求整体的应该想到区间信息合并和线段树上矩阵乘法。
定义 \(DP\) 状态 \(f_{i,0\sim 5}\) 表示到了第 \(i\) 应该开始匹配 \(\texttt{bessie}\) 的第 \(0\sim5\) 位(从 \(0\) 开始)。
那么转移为很好推。
那么怎么快速更改呢?
发现其实这个东西可以用矩阵转移。更改一个值就是更改他的转移矩阵。
用线段树即可!
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define int long long
namespace gtx{
// Fast IO
inline void read(int &x){
x = 0;int h = 1;char tmp;
do{tmp=getchar();if(tmp=='-')h*=-1;}while(!isdigit(tmp));
while(isdigit(tmp)) x*=10,x+=tmp-'0',tmp=getchar();
x*=h;
}
inline void read(char &x){do{x=getchar();}while(x==' '||x=='\n'||x=='\r');}
inline void write(char x){putchar(x);}
inline void write(int x){
if(x<0) putchar('-'),x=-x;int st[200]={0},tot=0;
do st[++tot]=x%10,x/=10; while(x);
while(tot){putchar(st[tot--]+'0');}
}
inline void write(int x,char y){write(x);write(y);}
#ifndef int
inline void read(long long &x){
x = 0;int h = 1;char tmp;
do{tmp=getchar();if(tmp=='-')h*=-1;}while(!isdigit(tmp));
while(isdigit(tmp)) x*=10,x+=tmp-'0',tmp=getchar();
x*=h;
}
inline void write(long long x){
if(x<0) putchar('-'),x=-x;int st[200]={0},tot=0;
do st[++tot]=x%10,x/=10; while(x);
while(tot){putchar(st[tot--]+'0');}
}
inline void write(long long x,char y){write(x);write(y);}
#endif
const int MAXN = 2e5+10;
long long B[10][10] =
{
{0,1,0,0,0,0,0,0,0},
{0,1,0,0,0,0,0,0,0},
{0,0,1,0,0,0,0,0,0},
{0,0,0,1,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0},
{0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,1,1,0},
{0,0,0,0,0,0,0,1,0},
{0,1,0,0,0,0,0,0,1}
}
;
long long E[10][10] =
{
{1,0,0,0,0,0,0,0,0},
{0,0,1,0,0,0,0,0,0},
{0,0,1,0,0,0,0,0,0},
{0,0,0,1,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0},
{1,0,0,0,0,0,1,1,0},
{0,0,0,0,0,0,1,1,0},
{0,0,0,0,0,0,0,1,0},
{1,0,0,0,0,0,0,0,1}
}
;
long long S[10][10] =
{
{1,0,0,0,0,0,0,0,0},
{0,1,0,0,0,0,0,0,0},
{0,0,0,1,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0},
{0,0,0,0,1,0,0,0,0},
{0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,1,1,0},
{0,0,0,0,0,0,0,1,0},
{1,0,0,0,0,0,0,0,1}
};
long long I[10][10] =
{
{1,0,0,0,0,0,0,0,0},
{0,1,0,0,0,0,0,0,0},
{0,0,1,0,0,0,0,0,0},
{0,0,0,1,0,0,0,0,0},
{0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,1,1,0},
{0,0,0,0,0,0,0,1,0},
{1,0,0,0,0,0,0,0,1}
}
;
long long T[10][10] =
{
{1,0,0,0,0,0,0,0,0},
{0,1,0,0,0,0,0,0,0},
{0,0,1,0,0,0,0,0,0},
{0,0,0,1,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0},
{0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,1,1,0},
{0,0,0,0,0,0,0,1,0},
{1,0,0,0,0,0,0,0,1}
}
;
struct segmentree{
int l,r;
long long ans[10][10];
}tree[MAXN<<3];
inline void pushup(int o){
memset(tree[o].ans,0,sizeof tree[o].ans);
for(int i = 0;i<9;i++){
for(int j = 0;j<9;j++){
for(int k = 0;k<9;k++){
tree[o].ans[i][k] += tree[o*2].ans[i][j]*tree[o*2+1].ans[j][k];
}
}
}
}
inline void build(int k,int l,int r){
tree[k].l = l;
tree[k].r = r;
if(l==r) return;
int mid = (l+r)>>1;
build(k*2,l,mid);
build(k*2+1,mid+1,r);
}
inline void modify(int k,int x,char t){
if(tree[k].l>x||tree[k].r<x) return;
if(tree[k].l==x&&tree[k].r==x){
if(t=='b') memcpy(tree[k].ans,B,sizeof B);
else if(t=='e') memcpy(tree[k].ans,E,sizeof E);
else if(t=='i') memcpy(tree[k].ans,I,sizeof I);
else if(t=='s') memcpy(tree[k].ans,S,sizeof S);
else memcpy(tree[k].ans,T,sizeof T);
return;
}
modify(k*2,x,t);
modify(k*2+1,x,t);
pushup(k);
}
char a[MAXN];
int n;
int begin[10]=
{0,0,0,0,0,0,0,0,1}
;
long long get(){
long long ans[10] = {};
for(int j = 0;j<9;j++){
for(int k = 0;k<9;k++)
ans[j] += begin[k]*tree[1].ans[k][j];
}
return ans[7];
}
signed main(){
scanf("%s",a+1);
n = strlen(a+1);
build(1,1,n);
for(int i = 1;i<=n;i++) modify(1,i,a[i]);
write(get(),endl);
int q;
read(q);
while(q--){
int x;char y;
read(x);read(y);
modify(1,x,y);
write(get(),endl);
}
return 0;
}
}
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
// ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int T = 1;
// gtx::read(T);
while(T--) gtx::main();
return 0;
}

浙公网安备 33010602011771号