CF833B The Bakery(线段树维护dp)
对于暴力dp来说,对于当前i位置分成j段的答案就是f[l][j-1]+val(l+1,i),对于所有符合条件的答案取一个max
我们发现暴力会t,并且是取max,想到用线段树维护前一次的dp,这样只要dpk次就能获得答案。
至于val,其实就是区间修改,对于每个点,他有贡献的区间就是离他相同最近的点到他本身的距离,因此用线段树优化dp
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int N=1e5+10; int n,k; int a[N]; int last[N]; int pre[N]; struct node{ int l,r; int mx; int lazy; }tr[N<<2]; int f[N]; void pushup(int u){ tr[u].mx=max(tr[u<<1].mx,tr[u<<1|1].mx); } void build(int u,int l,int r){ if(l==r){ tr[u]={l,r,0,0}; } else{ tr[u]={l,r,0,0}; int mid=l+r>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); pushup(u); } } void rebuild(int u,int l,int r){ tr[u].lazy=0; if(l==r){ tr[u].mx=f[l]; } else{ int mid=l+r>>1; rebuild(u<<1,l,mid); rebuild(u<<1|1,mid+1,r); pushup(u); } } void pushdown(int u){ int x=tr[u].lazy; tr[u<<1].mx+=x; tr[u<<1|1].mx+=x; tr[u<<1].lazy+=x; tr[u<<1|1].lazy+=x; tr[u].lazy=0; } void modify(int u,int l,int r,int x){ if(tr[u].l>=l&&tr[u].r<=r){ tr[u].mx+=1; tr[u].lazy+=1; return ; } if(tr[u].lazy){ pushdown(u); } int mid=tr[u].l+tr[u].r>>1; if(l<=mid) modify(u<<1,l,r,x); if(r>mid) modify(u<<1|1,l,r,x); pushup(u); } int query(int u,int l,int r){ if(tr[u].l>=l&&tr[u].r<=r){ return tr[u].mx; } if(tr[u].lazy) pushdown(u); int mid=tr[u].l+tr[u].r>>1; int ans=0; if(l<=mid){ ans=query(u<<1,l,r); } if(r>mid) ans=max(ans,query(u<<1|1,l,r)); return ans; } int main(){ ios::sync_with_stdio(false); cin>>n>>k; int i; for(i=1;i<=n;i++){ cin>>a[i]; pre[i]=last[a[i]]; last[a[i]]=i; } build(1,0,n); for(i=1;i<=k;i++){ rebuild(1,0,n); for(int j=i;j<=n;j++){ modify(1,pre[j],j-1,1); f[j]=query(1,0,j-1); } } cout<<f[n]<<endl; }
没有人不辛苦,只有人不喊疼