CF 868 F. Yet Another Minimization Problem

F. Yet Another Minimization Problem

http://codeforces.com/contest/868/problem/F

题意:

  给定一个长度为n的序列。你需要将它分为m段,每一段的代价为这一段内相同的数的对数,最小化代价总和。 n<=100000,m<=20。

分析:

  f[k][j]=min{f[k-1][j]+cost(k,j,i)};

  cost发现不能快速的算出。于是不能用类似单调队列+二分的方法来做了。

  考虑分治,solve(Head,Tail,L,R,w)当分治区间为Head,Tail,L,R为转移的区间,那么可以直接扫一遍找到转移的最优位置k,然后分治下去。分治的过程中,维护每个数出现了几次(cnt数组),在进入下一层的时候,更新了下层用到的区间的cnt。

代码:

 1 /*
 2 * @Author: mjt
 3 * @Date:   2018-10-15 11:28:17
 4 * @Last Modified by:   mjt
 5 * @Last Modified time: 2018-10-15 14:40:30
 6 */
 7 #include<cstdio>
 8 #include<algorithm>
 9 #include<cstring>
10 #include<cmath>
11 #include<iostream>
12 #include<cctype>
13 #include<set>
14 #include<vector>
15 #include<queue>
16 #include<map>
17 #define fi(s) freopen(s,"r",stdin);
18 #define fo(s) freopen(s,"w",stdout);
19 using namespace std;
20 typedef long long LL;
21 
22 inline int read() {
23     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
24     for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
25 }
26 
27 const int N = 100005;
28 
29 int a[N], cnt[N];
30 LL f[N], g[N];
31 
32 #define add(x) w += cnt[x], cnt[x] ++
33 #define del(x) cnt[x] --, w-= cnt[x]
34 
35 void solve(int Head,int Tail,int L,int R,LL w) { // w保存(L~R)中,非(Head~Tail),区间的值,即L~min(R,Head)。
36     if (Head > Tail) return ;
37     int mid  = (Head + Tail) >> 1, p = min(R, mid), k = 0;
38     for (int i=Head; i<=mid; ++i) add(a[i]);
39     for (int i=L; i<=p; ++i) {
40         del(a[i]); // 从i转移,所以i左边的数,不应该被算入贡献,所以要减去。
41         if (f[mid] > g[i] + w) f[mid] = g[i] + w, k = i;
42     }
43 
44     for (int i=Head; i<=mid; ++i) del(a[i]); 
45     for (int i=L; i<=p; ++i) add(a[i]);
46     solve(Head, mid - 1, L, k, w); 
47     
48     for (int i=L; i<k; ++i) del(a[i]); 
49     for (int i=Head; i<=mid; ++i) add(a[i]);
50     solve(mid + 1, Tail, k, R, w);
51     
52     for (int i=Head; i<=mid; ++i) del(a[i]); // 初始为递归进来时候的cnt数组。
53     for (int i=L; i<k; ++i) add(a[i]);
54 }
55 int main() {
56     int n = read(), k = read();
57     for (int i=1; i<=n; ++i) {
58         a[i] = read();
59         f[i] = f[i - 1] + cnt[a[i]];
60         cnt[a[i]] ++;
61     }
62     memset(cnt, 0, sizeof(cnt));
63     while (-- k) {
64         swap(f, g);
65         memset(f, 0x3f, sizeof(f));
66         solve(1, n, 1, n, 0);
67     }
68     cout << f[n];
69     return 0;
70 }
原文地址:https://www.cnblogs.com/mjtcn/p/9790888.html