codeforces750E New Year and Old Subsequence 矩阵dp + 线段树
思路:
先看一个大牛的题解
题解里面对矩阵的构造已经写的很清楚了,其实就是因为在每个字符串都有固定的很多中状态,刚好可以用矩阵来表达,所以$(i,j)$这种状态可以通过两个相邻的矩阵的$min(i,k)+(k,j)$得到,取最小值即可,由于这是一个区间问题,所以用线段树来维护区间的矩阵运算,这个运算就是取min的过程。
虽然这道原题被出在2019icpc南昌网络赛中了,但这个做法以前确实没有遇见过,开阔了思路。
代码和博客里的其实几乎一样。
#pragma GCC optimize (2) #pragma G++ optimize (2) #pragma comment(linker, "/STACK:102400000,102400000") #include<bits/stdc++.h> #include<unordered_map> #define rep(i,a,b) for(int i=a;i<=b;++i) #define dep(i,b,a) for(int i=b;i>=a;--i) #define clr(a,b) memset(a,b,sizeof(a)) #define pb push_back #define pii pair<int,int > using namespace std; typedef long long ll; ll rd() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int maxn = 2e5+10; int n,m; char s[maxn]; struct matrix{ int a[5][5]; matrix(){clr(a,0x3f);} matrix operator *(matrix x){ matrix res; rep(i,0,4){ rep(j,0,4){ rep(k,0,4){ res.a[i][j]=min(res.a[i][j],a[i][k]+x.a[k][j]); } } } return res; } }; struct Tree{ int l,r; matrix M; }t[maxn<<2]; void pushup(int x){ t[x].M=t[x<<1].M*t[x<<1|1].M; } void build(int x,int l,int r){ t[x].l=l,t[x].r=r;int mid=(l+r)>>1; if(l == r){ for(int i = 0; i < 5; i++) t[x].M.a[i][i] = 0; if(s[l] == '2') t[x].M.a[0][0] = 1, t[x].M.a[0][1] = 0; if(s[l] == '0') t[x].M.a[1][1] = 1, t[x].M.a[1][2] = 0; if(s[l] == '1') t[x].M.a[2][2] = 1, t[x].M.a[2][3] = 0; if(s[l] == '7') t[x].M.a[3][3] = 1, t[x].M.a[3][4] = 0; if(s[l] == '6') t[x].M.a[3][3] = 1, t[x].M.a[4][4] = 1; return; } build(x<<1,l,mid),build(x<<1|1,mid+1,r); pushup(x); } matrix query(int x,int l,int r){ if(l<=t[x].l&&t[x].r<=r)return t[x].M; int mid=(t[x].l+t[x].r)>>1; if(r<=mid)return query(x<<1,l,r); if(l>mid)return query(x<<1|1,l,r); return query(x<<1,l,r)*query(x<<1|1,l,r); } int main(){ int x,y; cin>>n>>m>>(s+1); build(1,1,n); rep(i,1,m){ scanf("%d%d",&x,&y); matrix ans=query(1,x,y); printf("%d\n",ans.a[0][4]>n?-1:ans.a[0][4]); } }
——愿为泰山而不骄
qq850874665~~