P4223彩色方块
时间限制 : - MS 空间限制 : 131072 KB
评测说明 : 单个测试点1000ms
问题描述
何老板最近在玩一款叫“彩色方块”的小游戏,游戏虽然简单,但何老板仍旧乐此不疲。
游戏中有n个彩色方块成一排,方块的颜色用字母表示,给出目标排列,只要把它们排成跟目标一样的排列,就算过关。每次操作只能交换相邻两个方块。
给出一关游戏,何老板想知道,最少操作几次就能过关,请你帮他计算最少所需的操作步数。
输入格式
第一行,一个整数n表示方块的数量
第二行,n个大写字母,表示一开始方块的排列情况。每个字母表示对应方块的颜色。
第三行,n个大写字母,表示方块的目标排列情况。
输出格式
一行,一个整数,表示计算结果。
数据保证有解。
样例输入 1
3
RGB
GBR
样例输出 1
2
样例输入 2
4
AACB
BACA
样例输出 2
4
提示
样例1解释:
RGB -> GRB -> GBR
对于100%的数据:2<=n<=1,000,000
由于我用的是非主流方法,所以并不建议随意学习
题解
思路
使用分治的思想
要保证每一步都是最优解,就要保证第一步是最优解
我们选择把第一个方块移到正确的位置上
那么我们需要移动的步数=第一个方块在正解中的第一个位置-当前位置
例如
ABABA
正解BBAAA
那么第一个字母A当前位置为1,移动到正确位置为3,那么它移动的步数就是2
那么移动到正确位置之后,当前字符串变为了
BAABA
如果说原本在A错误位置右边正确位置左边的字符想要移动到A的正确位置的左边时,就可以少移动一次
例如
ABCDE
正解EDCBA
A移动到正确位置之后,变为了BCDEA
此时,如果D想要移动到移动到正确位置,原本是需要2步,而此时只需要1步
那么如何判定减少的步数呢??
由于太难证明,我就直接写结论了
我们在已经被占的正确位置下标上1
那么当前字符的位置在当前讨论上一定为1
最小移动次数=正确位置-1-正确位置前的标记总数
标记
添加标记只需要用简单的树状数组标记
而如何快速得到当前字符串的正确位置呢??
详见另一篇NKOJ-3703
另外
据说逆序对做起简单易懂而且跑起飞快
附上代码
#include <iostream>
#include <cstdio>
using namespace std;
int n,U[1123456],star[123],nxt[1123456];
char A[1123456],C[1123456];
long long res=0;
int lowbit(int x){return x&-x;}
void add(int x){for(int i=x;i<=n;i+=lowbit(i))U[i]++;}
int find(int x)
{
int ans=0;
for(int i=x;i;i-=lowbit(i))ans+=U[i];
return ans;
}
int main()
{
//freopen("In.txt","r",stdin);
int s;
scanf("%d\n",&n);
for(int i=1;i<=n;i++)A[i]=getchar();
for(int i=0;i<=n;i++)C[i]=getchar();
for(int i=n;i;i--)
{
nxt[i]=star[C[i]];
star[C[i]]=i;
}
for(int i=1;i<=n;i++)
{
s=star[A[i]];
res+=s-find(s)-1;
add(s);
star[A[i]]=nxt[s];
}
printf("%lld",res);
}