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 }