Part1. 题目概述

你有一个数组,其顺序可以随意打乱

你可以在其中任选一个数增加 kk,这记为一次操作。

求最小的操作次数以使原数组为回文串

若不可能,输出 1-1

Part2. 思路

  1. 首先看到只能增加 kk,说明无论怎么操作,每个数除以 kk 的余数不变

  2. 要求得到回文串顺序可以随意打乱,也就是说只要满足最终的数组只有不大于一个数出现的次数为奇数(这个数拿一个放中间,其他的左右两边对称放)。

  3. 再回到1,除以 kk 的余数不变,那么可以对除以 kk 的每一个余数分组,每一组都可以通过加 kk 使得数字相等,比如这组样例的分组:

    1
    2
    13 3
    2 3 9 14 17 10 22 20 18 30 1 4 28
    余数为 00 余数为 11 余数为 22
    3,9,18,303,9,18,30 10,22,1,4,2810,22,1,4,28 2,14,17,202,14,17,20
  4. 如何判断是否可行呢?只需要统计所有组中有几组的元素个数为奇数(如上面的分组中余数为 11 组就是奇数个数,必须把这个组中的某个元素放到新序列的中心位置),如果个数大于 11,就一定是不可行的(不可能把多个元素放到中心)。

  5. 接下来开始构造最优解。可以很清楚地发现,要在每一组中构造出相等的元素对(一个放前面,一个放后面),比如上余数为 11 组可以这样构造:

    (2,14),(17,20)(2,14),(17,20)

    只要:

    (2+3×4,14),(17+3×1,20)(2+3\times4,14),(17+3\times1,20)

    进行了 4+1=54+1=5 次操作。

    如果是奇数个元素的组,只需要取出一个元素放在中心后,变成偶数个元素的组即可。

  6. 如何使解最优呢?可以先尝试把每一个组内排个序:

    余数为 00 余数为 11 余数为 22
    3,9,18,303,9,18,30 1,4,10,22,281,4,10,22,28 2,14,17,202,14,17,20

    这时候要分类讨论一下了:

    • 一组中有偶数个元素。

      从小到大,每次取两个,一定最优。(否则如果跨过中间的元素找,一定会需要更多操作,你可以自己模拟一下)。

    • 一组中有奇数个元素。

      很快想到朴素方法:枚举要去除的元素,求出去除每个元素下的最优,时间复杂度 O(n2)O(n^2)

      这显然会超时,但大致思路是可行的,求出下一个解时很多计算是和上一个重复的,这就有了优化的方法。下图七个数的情况:

      不取 第一组 第二组 第三组
      1$ (2,3)(2,3) (4,5)(4,5) (6,7)(6,7)
      2$ (1,3)\color{red}(1,3) (4,5)(4,5) (6,7)(6,7)
      3$ (1,2)\color{red}(1,2) (4,5)(4,5) (6,7)(6,7)
      4$ (1,2)(1,2) (3,5)\color{red}(3,5) (6,7)(6,7)
      5$ (1,2)(1,2) (3,4)\color{red}(3,4) (6,7)(6,7)
      6$ (1,2)(1,2) (3,4)(3,4) (5,7)\color{red}(5,7)
      7$ (1,2)(1,2) (3,4)(3,4) (5,6)\color{red}(5,6)

      红色的就是变化的部分,这样就解出了(分奇偶讨论一下)。

      f(ai+1)={f(ai)(ai+2ai+1)k+(ai+2ai)k,i1(mod2)f(ai)(ai+1ai1)k+(aiai1)k,i0(mod2)f(a_{i+1})= \begin{cases}f(a_i)-\dfrac{(a_{i+2}-a_{i+1})}{k}+\dfrac{(a_{i+2}-a_i)}{k},\quad &&i \equiv 1 \pmod 2\\f(a_i)-\dfrac{(a_{i+1}-a_{i-1})}{k}+\dfrac{(a_{i}-a_{i-1})}{k},\quad&&i \equiv 0 \pmod 2\end{cases}

      问题解决!

Part3. Code

注意数据范围,存余数等可以开映射解决,也可以离散化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include<bits/stdc++.h>
using namespace std;
int n,a[100001];
int mod[100001];
int k;
map<int,int>mds;
map<int,int>vis;
vector<int>modn;
map<int,vector<int> >md;
void solve(){
cin>>n>>k;
mds.clear();
md.clear();
for(int i=1;i<=n;i++){
cin>>a[i];
mod[i]=a[i]%k;
mds[mod[i]]+=1;
md[mod[i]].push_back(a[i]);
}
vis.clear();
modn.clear();
int ss=0;
for(int i=1;i<=n;i++){
if(vis[mod[i]]==0){
modn.push_back(mod[i]);
if(mds[mod[i]]%2)ss++;
vis[mod[i]]=1;
}
}
if(ss>=2){
cout<<-1<<endl;
return;
}
int sum=0;
for(auto i:modn){
sort(md[i].begin(),md[i].end());
if(md[i].size()%2){
int ss=0,s=0x3f3f3f3f;
for(int j=1;j+1<md[i].size();j+=2){
ss+=(md[i][j+1]-md[i][j])/k;
}
s=ss;
for(int j=0;j+1<md[i].size();j++){
if(j%2){
ss=ss-(md[i][j+1]-md[i][j-1])/k+(md[i][j]-md[i][j-1])/k;
}
else{
ss=ss-(md[i][j+2]-md[i][j+1])/k+(md[i][j+2]-md[i][j])/k;
}
s=min(s,ss);
}
sum+=s;
}
else{
for(int j=0;j+1<md[i].size();j+=2){
sum+=(md[i][j+1]-md[i][j])/k;
}
}
}
cout<<sum<<endl;
}
int main(){
int t;
cin>>t;
while(t--)solve();
return 0;
}