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 }
D