NKOJ-4223 彩色方块

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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值