求循环串的最小逆序数 HDUOJ 1394

题目链接在此
题意是给定一个长度n, 再给一个[0,n-1]的排列, 可以循环地将第一个数放置序列末尾, 问这样循环出来的所有序列中, 最小的逆序数是多少?

思路:

  • 先求得原序列串的逆序数
  • 对于在当前序列串的第一个数字来说,贡献的逆序数是第一位后小于自己的数的数量即 a[1] - 1
  • 将第一个数字放到末端, 原先在第一位贡献逆序数量被消除,在末尾贡献的逆序数是在末位前大于自己的数的数量, 即 n - a[1]

举个莉子, 序列3 1 4 5 2中, 3贡献的逆序数自然是3 - 1 = 2个(小于自己又在后面(必然在自己后面)的)
3若到末位, 成1 4 5 2 3, 贡献的则是5 - 3 = 2个(大于自己的又在前面(必然在自己前面)的)

综上所述, 每次循环,相当于当前原序列的总逆序数, 设为cur, 减去消失的逆序数 + 新添加的,得
cur = cur - (a[1] - 1) + (n - a[1])
至于如何求得原序列的逆序数,方法很多,常用的有归并排序/线段树/这里使用的树状数组
注意, 如果是树状数组,由于编号是[1,n] 所以对于a[i],值域也得是[1,n] 题目是[0,n-1],所以读取后得++a[i]调一下

#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;

const int maxN = 5005, inf = 0x3f3f3f3f;
int A[maxN], bit[maxN + 1], N;
void add(int i, int x) { while (i <= N) { bit[i] += x; i += i & -i; } }
int sum(int i) { int ret = 0; while (i) { ret += bit[i]; i -= i & -i; } return ret; }

int main() {
    // freopen("data.in", "r", stdin);
    while (~scanf("%d", &N) && N) {
        memset(bit, 0, sizeof(bit));
        int inv = 0;
        for (int i = 0; i < N; ++i) {
            scanf("%d", &A[i]);
            ++A[i];
            inv += i - sum(A[i]);
            add(A[i], 1);
        }

        int ans = inv, u, v;
        for (int i = 0; i < N; ++i) {
            u = A[i] - 1;
            v = N - A[i];
            inv = inv - u + v;
            ans = min(ans, inv);
        }
        printf("%d\n", ans);
    }
    return 0;
}
附加:

到最后..还是想稍微整理一下,为什么树状数组能求逆序数?
首先可能得先了解一下什么是树状数组,长什么样,节点怎么和数组下标对应起来,这个这里就不打算说了..树状数组的一般操作:

给定i, 计算a1 + a2 + a3 + ... an
给定i和x, 执行 ai += x

在知道这个数据结构的前提下:
求某个逆序数,即求在自己前面又大于自己的数量,那我们只需要:
在当前数字下标是j, 值是a[j], 在数组里做个小标记,比如 bit[a[j]] += 1, 假设所有数都这样处理过,那以后我们要统计当前数字下有多少个小于自己的数字呢? 求个前n项和就可以了, 那大于自己的数字呢?自然就是n - sum(a[j])啦..把每个数字的逆序数一加,得解

到这儿..其实思想可以再简单化一些: 你就想象你有一个标记数组vis[maxN] = {0} ,每当一个数字a[j], 出现了就vis[a[j]] = 1, 那你要统计前面有多少个小于自己的数字,相当于统计j前面出现了多少个1(求前n项和),设为sum,那么n - sum,就是当前数字贡献的逆序数了。 但是这种方法效率很低,只不过利用了树状数组来加速,思路是一样的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342