题目大意:给你一个序列(n<=35000),最多分不大于m块(m<=50),求每个块内不同元素的数量之和的最大值
考试的时候第一眼建图,没建出来,第二眼贪心 ,被自己hack掉了,又随手写了个dp方程,感觉可以用splay维护,发现有区间操作并可取,又发现这个方程只能用线段树维护,然后只剩40min了我没调完,而且线段树打错了......
定义f[i][j]表示以第i个数为这个块的结尾,已经分了j块的答案的最大值
很明显,sum表示不同元素数量
对于每个元素,记录一个表示数 i 上一次出现的位置,那么遍历到i的时候,能更新sum的位置只有到i-1,线段树维护区间修改!
而最大,线段树维护区间最大值!
算好空间,建立M颗线段树即可
1 #include <vector> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define ll long long 6 #define ull unsigned long long 7 #define il inline 8 #define mod 905229641 9 #define N 35100 10 #define M 52 11 #define il inline 12 using namespace std; 13 //re 14 int n,m; 15 int a[N],lst[N]; 16 int f[N][M]; 17 struct Seg{ 18 int ma[N<<2],tag[N<<2]; 19 il void pushup(int rt){ma[rt]=max(ma[rt<<1],ma[rt<<1|1]);} 20 il void pushdown(int rt){ 21 if(tag[rt]) 22 ma[rt<<1]+=tag[rt],ma[rt<<1|1]+=tag[rt], 23 tag[rt<<1]+=tag[rt],tag[rt<<1|1]+=tag[rt],tag[rt]=0;} 24 void update(int L,int R,int l,int r,int rt,int w) 25 { 26 if(L>R) return; 27 if(L<=l&&r<=R){ma[rt]+=w;tag[rt]+=w;return;} 28 pushdown(rt); 29 int mid=(l+r)>>1; 30 if(L<=mid) update(L,R,l,mid,rt<<1,w); 31 if(R>mid) update(L,R,mid+1,r,rt<<1|1,w); 32 pushup(rt); 33 } 34 int query(int L,int R,int l,int r,int rt) 35 { 36 if(L>R) return 0; 37 if(L<=l&&r<=R) {return ma[rt];} 38 pushdown(rt); 39 int mid=(l+r)>>1,ans=0; 40 if(L<=mid) ans=max(query(L,R,l,mid,rt<<1),ans); 41 if(R>mid) ans=max(query(L,R,mid+1,r,rt<<1|1),ans); 42 pushup(rt); 43 return ans; 44 } 45 }s[M]; 46 int main() 47 { 48 freopen("handsome.in","r",stdin); 49 freopen("handsome.out","w",stdout); 50 scanf("%d%d",&n,&m); 51 for(int i=1;i<=n;i++) 52 scanf("%d",&a[i]); 53 for(int i=1;i<=n;i++) 54 { 55 for(int j=1;j<=m;j++) 56 s[j-1].update(lst[a[i]],i-1,0,n,1,1), 57 f[i][j]=s[j-1].query(0,i-1,0,n,1), 58 s[j].update(i,i,0,n,1,f[i][j]); 59 lst[a[i]]=i; 60 } 61 int ans=0; 62 for(int i=1;i<=m;i++) 63 ans=max(ans,f[n][i]); 64 printf("%d ",ans); 65 return 0; 66 }