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;
}

sjtu1590 强迫症的更多相关文章

  1. 有强迫症的我只能自己写一个json格式化工具

    缘由 为什么博客园的markdown解析出问题了啊?好奇怪啊! 一直以来在编码规范界有2大争论不休的话题,一个是关于是用空格缩进还是tab缩进的问题,一个是花括号是否换行的问题,笔者是tab缩进和花括 ...

  2. Swift 吐槽下Swift里一个逼死强迫症的语法:中缀语法

    中缀语法是OC里特有的一种,就是在函数的参数前面加一个解释词,让调用的时候明白该参数的含义 比如: -(void)processDataWithparamaA:(NSString *)paramaA ...

  3. 强迫症和拖延症患者如何应对马桶4(遨游Maxthon)“上次未关闭页面”丢失的问题

    强迫症和拖延症患者如何应对马桶4(遨游Maxthon)“上次未关闭页面”丢失的问题 用了马桶好多年,虽然一直bug不断,经常假死丢数据坑爹什么的,但是总得来说还是略有感情,不忍舍弃. 马桶一直有一个好 ...

  4. [逼死强迫症 - C&C++设计风格选择.1] : 命名规范

    1.命名规范 本系列的第一篇,命名风格本就是有关艺术审美,没有美与丑的绝对标准,本文难免带有主观选择倾向,但是会尽量保持客观的态度归纳几种主流的命名风格,仅供参考.制定规范是为了方便团队沟通和利于代码 ...

  5. (亲测)躺着破解IDM下载权限,治疗不用破解补丁的强迫症们

    首先.如果触犯了某些规则权限,请原谅. 很早以前就做过这个的破解,挺实用的,我今天就把之前写的经验贴出来大家一起学习学习~~~ 今天利用这个方法破解了最新版,最终的效果如下所示:我不是来刷存在感的.只 ...

  6. 【BZOJ5505】[GXOI/GZOI2019]逼死强迫症(矩阵快速幂)

    [BZOJ5505][GXOI/GZOI2019]逼死强迫症(矩阵快速幂) 题面 BZOJ 洛谷 题解 如果没有那两个\(1*1\)的东西,答案就是斐波那契数,可以简单的用\(dp\)得到. 大概是设 ...

  7. [LOJ3086][GXOI/GZOI2019]逼死强迫症——递推+矩阵乘法

    题目链接: [GXOI/GZOI2019]逼死强迫症 设$f[i][j]$表示前$i$列有$j$个$1*1$的格子的方案数,那么可以列出递推式子: $f[i][0]=f[i-1][0]+f[i-2][ ...

  8. P5303 [GXOI/GZOI2019]逼死强迫症

    题目地址:P5303 [GXOI/GZOI2019]逼死强迫症 这里是官方题解 初步分析 从题目和数据范围很容易看出来这是一个递推 + 矩阵快速幂,那么主要问题在于递推的过程. 满足条件的答案一定是以 ...

  9. [JZOJ4786]小a的强迫症

    [JZOJ4786]小a的强迫症 题目大意: 有\(n(n\le10^5)\)种颜色的珠子,第\(i\)种颜色有\(num[i]\)个.你要把这些珠子排成一排,使得第\(i\)种颜色的最后一个珠子一定 ...

随机推荐

  1. url解析

    window.onload=function(){ var url="http://www.qq.com/index.html?key1=1&key2=2&key3=3&qu ...

  2. D3中path各指令的含义

    svg.append('path').attr({ id: 'mypath', d: 'M50 100Q350 50 350 250Q250 50 50 250' }) path 的指令有: 指令 参 ...

  3. 有关于Algorithm的基础介绍

    Niklaus Wirth:Algorithm + Data Structures = Programs 这句话呢,觉得很正确,算法和程序是不同的概念,算法的思想呢有递推,枚举,分治,贪婪,试探法,模 ...

  4. 【转】Emmagee app性能测试工具使用教程

    简介 Emmagee是网易杭州研究院QA团队开发的一个简单易上手的Android性能监测小工具,主要用于监控单个App的CPU,内存,流量,启动耗时,电量,电流等性能状态的变化,且用户可自定义配置监控 ...

  5. 我的VIM.rc

    我的VIM.rc """""""""""""""& ...

  6. C++实用数据结构:二叉索引树

    看下面这个问题(动态连续和查询): 有一个数组A(长度为n),要求进行两种操作: add(i,x):让Ai增大x: query(a,b):询问Aa+Aa+1+...+Ab的和: 若进行模拟,则每次qu ...

  7. bzoj2287:[POJ Challenge]消失之物

    思路:首先先背包预处理出f[x]表示所有物品背出体积为x的方案数.然后统计答案,利用dp. C[i][j]表示不用物品i,组成体积j的方案数. 转移公式:C[i][j]=f[j]-C[i][j-w[i ...

  8. HeadFirst设计模式-前言总结

    1 鸭子抽象类 class Duck { quack(); swim(); virtual display()=0 }; 现在如果让鸭子能够飞 class Duck { quack(); swim() ...

  9. linux命令行下命令参数前的一横(-)和两横(--)的区别

    原文转自:http://blog.csdn.net/songjinshi/article/details/6816776 在解释这些区别之前我们先了解一下有关linux的背景知识,这个需要大家先认真看 ...

  10. 九度OJ 1385 重建二叉树

    题目地址:http://ac.jobdu.com/problem.php?pid=1385 题目描述: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都 ...