AB都是水题。
C,设A和B是输入的最终分数,A和B一定具有这样的形式:A=a*b*b, B=a*a*b。那么A*B开三次方得到a*b,从而得到a和b,只要a和b存在答案便存在。开三次方使用二分即可。
D题,题意是使序列刚好分成k段,每段的贡献值为这段不同数字的个数,问一种分法使得分数最大,求最大的分数。在扫描的过程中,假设当前出现的数字是now,last[now]表示的是now上次出现的位置(如果之前没出现,那么这个值为0)-->每次出现一个值,就使得[last[a[i]]+1, i]成段更新值+1,那么在 i 这个位置时,对于任意的 j ,[j, i]出现的不同的数字的个数是 j 这个位置的值。有了这个以后,考虑dp的转移,设dp[i][j]表示到 i 这个位置,使用了 j 段的最大答案,那么转移为:
max{dp[o][k-1]+[o+1, j]的不同数字个数 | 1<=o<=i-1}
对于一个位置 i ,利用前面所提的方法的话,是需要下一个位置的值c[i+1]和dp[i][k-1],这样暴力扫描的话转移复杂度是线性的,不能承受。如果能使得 i 和 i+1位置相同,那么便能使用一个值g[i] = c[i] + dp[i][k-1],之后再利用线段树查询区间g的最大值来更新当前的dp值了。因此考虑之前的方法更新位置错开一位,在[last[a[i]], i-1]的位置成段更新那么现在c[i]的值就表示之前c[i+1]的值了,然后转移dp并维护线段树中g的最大值即可。时间复杂度O(n*k*lg(n))。同时可以利用滚动数组压缩空间。具体见代码:
1 #include <bits/stdc++.h> 2 #define t_mid (l+r>>1) 3 #define ls (o<<1) 4 #define rs (o<<1|1) 5 #define lson ls,l,t_mid 6 #define rson rs,t_mid+1,r 7 using namespace std; 8 const int N = 35000 + 5; 9 typedef long long ll; 10 11 int n, k; 12 int a[N]; 13 int last[N]; 14 int p[N]; 15 int dp[2][N]; 16 int c[N<<2], lazy[N<<2]; 17 void up(int o) {c[o] = max(c[ls], c[rs]);} 18 void down(int o) 19 { 20 if(lazy[o]) 21 { 22 c[ls] += lazy[o]; lazy[ls] += lazy[o]; 23 c[rs] += lazy[o]; lazy[rs] += lazy[o]; 24 lazy[o] = 0; 25 } 26 } 27 void updateForSeg(int o,int l,int r,int ql,int qr,int dt) 28 { 29 if(l == ql && r == qr) 30 { 31 c[o] += dt; 32 lazy[o] += dt; 33 return ; 34 } 35 down(o); 36 if(t_mid >= qr) updateForSeg(lson,ql,qr,dt); 37 else if(ql > t_mid) updateForSeg(rson,ql,qr,dt); 38 else 39 { 40 updateForSeg(lson,ql,t_mid,dt); 41 updateForSeg(rson,t_mid+1,qr,dt); 42 } 43 up(o); 44 } 45 void updateForPt(int o,int l,int r,int pos,int dt) 46 { 47 if(l == r) 48 { 49 c[o] += dt; 50 return ; 51 } 52 down(o); 53 if(t_mid >= pos) updateForPt(lson,pos,dt); 54 else updateForPt(rson,pos,dt); 55 up(o); 56 } 57 int query(int o,int l,int r,int pos) 58 { 59 if(l == r) return c[o]; 60 down(o); 61 if(pos <= t_mid) return query(lson,pos); 62 else return query(rson,pos); 63 } 64 65 int main() 66 { 67 cin >> n >> k; 68 for(int i=1;i<=n;i++) 69 { 70 scanf("%d",a+i); 71 int now = a[i]; 72 p[i] = last[now]; 73 last[now] = i; 74 } 75 int now = 0, pre = 1; 76 for(int i=1;i<=k;i++) 77 { 78 swap(now, pre); 79 memset(c,0,sizeof c); 80 memset(lazy,0,sizeof lazy); 81 for(int j=1;j<=n;j++) 82 { 83 updateForSeg(1,0,n,p[j],j-1,1); 84 updateForPt(1,0,n,j,dp[pre][j]); 85 dp[now][j] = c[1]; 86 } 87 } 88 printf("%d\n",dp[now][n]); 89 return 0; 90 }