Description

BS96发布了一套有\(m\)个band柄绘的新badge,kuma先生想要拿到04的badge于是进行了抽抽抽。

kuma先生一共抽了\(n\)个badge。他把所有的badge排成一排来统计战果,出于强迫症他希望把所有相同band的badge放在一起。

kuma先生整理badge的方法是交换\(2\)个相邻的badge,现在他想知道他最少交换多少次可以达成目的。

Input

有多组数据,第一行一个数\(T\)表示数据组数,接下来每组数据有\(2\)行。

第一行两个数\(n,m\)。

第二行\(n\)个数,表示每个badge的band编号。

Output Format

对于每组数据输出一行,形如”Case #X:Y”。X为数据组数,从\(1\)开始,\(Y\)为最少的交换次数。

Sample Input

3

4 2

1 2 1 2

6 4

2 1 4 3 1 2

8 6

1 3 2 5 5 4 5 2

Sample Output

Case #1: 1

Case #2: 6

Case #3: 5

Hints

\(40\%\), \(n \le 100, m \le 6\)

\(70\%\), n \le 1000, m \le 14

\(100\%\), n \le 100000, m \le 18

首先确定了最后次序之后,这个过程就是一个冒泡排序,最少交换次数即为逆序对数目。于是这道题目便变成了一道排列dp题目。我们可以用状态压缩dp来解决。

设\(f[i]\)为状态为\(i\)时的最少逆序对数目。这个状态表示若\(a\)在集合\(i\)中,则\(a\)的最终位置已经确定排在前面一块。现在考虑我们将一个不在集合中的元素\(b\)加入集合中,我们只需要考虑\(b\)对逆序对的贡献,由于集合中已有元素最后都会放在\(b\)的前面,所以我们只需要计算在原数组中有多少\((p,q),q>p\),其中\(A_p = b,A_q \in i\)。对于特定的\(a,b\),这个贡献是可以预处理的。于是算法复杂度\(O(TM^22^M)\)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std; typedef long long ll;
#define maxn (100010)
#define maxm (20)
int T,N,M,A[maxn],suf[maxm][maxn],tmp[maxm]; ll f[1<<maxm],cor[maxm][maxm]; bool vis[1<<maxm]; inline int read()
{
int F = 1,ret = 0; char ch;
do ch = getchar(); while (!(ch >= '0'&&ch <= '9')&&ch != '-');
if (ch == '-') F = -1,ch = getchar();
do ret = ret*10+ch-'0',ch = getchar(); while (ch >= '0'&&ch <= '9');
return F*ret;
} inline void ready()
{
for (int i = 1;i <= N;++i) suf[A[i]][i] = 1;
for (int i = N-1;i;--i) for (int j = 1;j <= M;++j) suf[j][i] += suf[j][i+1];
for (int i = 1;i <= N;++i)
for (int j = 1;j <= M;++j)
cor[A[i]][j] += (ll)suf[j][i];
} inline void init()
{
memset(vis,false,1<<M);
memset(cor,0,sizeof(cor));
for (int i = 1;i < (1<<M);++i) f[i] = 1LL<<50;
for (int i = 0;i < M;++i) f[1<<i] = 0;
for (int i = 1;i <= M;++i) memset(suf[i],0,4*(N+1));
for (int i = 0;i < M;++i) vis[1<<i] = true;
} int main()
{
freopen("1590.in","r",stdin);
freopen("1590.out","w",stdout);
T = read();
for (int Case = 1;Case <= T;++Case)
{
printf("Case #%d: ",Case);
N = read(),M = read(); init();
for (int i = 1;i <= N;++i) A[i] = read();
ready();
for (register int i = 1,nn;i < (1<<M)-1;++i)
{
nn = 0;
for (register int j = 0;j < M;++j) if (i&(1<<j)) tmp[++nn] = j+1;
for (register int j = 0;j < M;++j)
if (!(i&(1<<j)))
{
ll sum = f[i];
for (register int k = 1;k <= nn;++k) sum += cor[j+1][tmp[k]];
if (f[i|(1<<j)] > sum) f[i|(1<<j)] = sum;
}
}
printf("%lld\n",f[(1<<M)-1]);
}
fclose(stdin); fclose(stdout);
return 0;
}