## 2018 ACM-ICPC 中国大学生程序设计竞赛线上赛 D Merchandise (斜率优化)

Description:

The elderly aunts always like to look for bargains and preferential merchandise. Now there is a shop carrying out group purchase promotion.

Rules are as follows :

There are r pieces of promotional items, and each item is only one. In the group purchase, everyone will buy one at least and have to conform the rules if they want to enjoy the benefit :

All the merchandise at a prime number has to be purchased ;

the merchandise at non-prime number cannot be chosen ;

The amount of money that each person pays is the square of the difference between the maximum and minimum value of the product he or she chooses. Notice that there may be several merchandises having the same price.

(If a person buys only one item, follow the rules and spend ￥ 0.00)

Assume that there are m people in a group, and everyone should enjoy the benefit.  Please arrange each person's choice of merchandise reasonably, so that the sum of money paid by m people can be the minimum.

The number of merchandise with a price of prime is n，m<=n , n<=5000

Input:

The input contains multiple test cases.

In the first line of the input there’s an integer T which is the number of test cases. Then the description of T test cases will be given.

For any test case, the first line of input consists of two separated integers r and m.

（0<r<=100000,0<m<=1000)

The second line should consists of r space separated integers k1,k2...kr.

(1<ki<2^16,1<=i<=r)

ki represents the price of one item

Output:

Print one integer sum (sum<2^32)——the minimum number of money paid by m people.

#### 样例输入

```1
6 2
5 6 10 2 11 3 ```

#### 样例输出

`9题意：给你r个数，首先你要跳出其中的所有的素数(n<=5000)，然后将这些素数分成m(<=n)个组。每个组的权值为（这个组中最大元素-这个组最小元素）^2，问各组的权值的和最小为多少思路：首先我们想到先排序然后dp先写转移方程，我们设dp[i][j]为前i个数分为j个集合的权值的和，则有$\dpi{80} \huge dp[i][j]=min_{(1\leq k\leq i)} {\begin{Bmatrix} dp[k-1][j-1]+(a[i]-a[k])^{2} \end{Bmatrix}}$还有j那维需要循环，这个复杂度为n^3，显然是超时的。我们利用斜率优化算法优化成n^2的将上面等式右边{}内的看成关于自变量k的一个函数val(k)将k1带入得到$\inline \dpi{100} \huge dp[i][j]=dp[k_{1}-1][j-1]+(a[i]-a[k_{1}])^{2}$将k看成变量，分离变量与常量整理上式。$\inline \dpi{100} dp[k-1][j-1]+a[k]^{2}=(2*a[i]) * a[k]+(dp[i][j]+a[i]^{2})$$\inline \dpi{100} \huge y=kx+b$这个式子就像一个直线的方程，我们的目的是让dp[i][j]最小即截距dp[i][j]+a[i]^2最小这个直线的斜率是2*a[i]，也就是说坐标轴上有好多点，每个具体的k都确定一个点(a[k],dp[k-1][j-1]+a[k]^2)我们转化成了一个线性规划问题，假设当前有好几个点，我们只需要维护他们是下凸的就行了即如果有三个连续的点k1,k2,k3，那么斜率(k2,k1)<斜率(k2,k3)这个我们用单调队列维护。这样就能减少复杂度代码如下：`
``` 1 #include <bits/stdc++.h>
2
3 using namespace std;
4 typedef long long ll;
5 const ll inf = 1e18;
6 const int maxn = 5e5+5;
7 const int maxm = 5010;
8 const int maxp = (1<<16)+500;
9 bool check (int x){
10     for (int i=2;i*i<=x;++i){
11         if (x%i==0)
12             return false;
13     }
14     return true;
15 }
16 int prime[maxp];//保存素数
17 bool vis[maxp];
18 int r,n,m;
19 ll a[maxn],b[maxm];
20 ll dp[maxm][maxm];
21 int q[maxm];
23 void  getprime(int n)
24 {
25     int cnt = 0;
26     memset(vis,0,sizeof(vis));
27     for(int i=2;i<n;i++)
28     {
29         if(!vis[i])
30         prime[cnt++]=i;
31         for(int j=0;j<cnt&&i*prime[j]<n;j++)
32         {
33             vis[i*prime[j]]=1;
34             if(i%prime[j]==0)
35             break;
36         }
37     }
38     return ;
39 }
40 int main()
41 {
42     int t;
43     getprime(maxp);
44     //freopen("de.txt","r",stdin);
45     scanf("%d",&t);
46     dp[0][0]=0;
47     for (int i=1;i<maxm;++i)
48         dp[i][0]=inf;
49     while (t--){
50         n = 0;
51         ll x;
52         scanf("%d%d",&r,&m);
53         for (int i=0;i<r;++i){
54             scanf("%lld",&x);
55             if (!vis[x]){
56                 a[++n] = x;
57             }
58         }
59         sort(a+1,a+n+1);
60         for (int i=1;i<=n;++i){
61             b[i] = a[i]*a[i];
62         }
63         for (int j=1;j<=m;++j){
64             head = 0,tail = 0;
65             dp[0][j]=inf;
66             for (int i=1;i<=n;++i){
68                     int x=q[tail-2],y=q[tail-1];
69                     double v1,v2;
70                     if (a[y]==a[x]) v1 = inf;
71                     else v1=(dp[y-1][j-1]+b[y]-dp[x-1][j-1]-b[x])/(2.0*(a[y]-a[x]));
72                     if (a[y]==a[i]) v2 = inf;
73                     else v2=(dp[i-1][j-1]+b[i]-dp[y-1][j-1]-b[y])/(2.0*(a[i]-a[y]));
74                     if (v1>v2)
75                         tail--;
76                     else
77                         break;
78                 }
79                 q[tail++] = i;
82                     double v1;
83                     if (a[y]==a[x]) v1 = inf;
84                     else v1=(dp[y-1][j-1]+b[y]-dp[x-1][j-1]-b[x])/(2.0*(a[y]-a[x]));
85                     if (v1<a[i])
87                     else
88                         break;
89                 }
91                 dp[i][j] = dp[tmp-1][j-1]+(ll)(a[i]-a[tmp])*(ll)(a[i]-a[tmp]);
92                 //printf("%lld %d %d\n",dp[i][j],i,j);
93             }
94         }
95         printf("%lld\n",dp[n][m]);
96     }
97     return 0;
98 }```

` `