Noip 2013 火柴排队 - 逆序对 - 转化思想

22 篇文章 0 订阅

当a列火柴和对应的每一个b列火柴差值最小时,答案最小

也就是说,需要a列的第i高火柴与b列的第i高火柴在位置上相对应

但是这个操作比较难想,我们不妨设f(x)为a列第x高火柴的位置在b列中的位置(注意,x指a列第x高火柴在a列的位置,而f(x)是位置与位置的映射)。这十分相似于求最长公共子序列的nlogn做法

而目标转化为使f[x] = x,也就是说把f(x)转换为一个递增序列,那么转换的操作次数就是f(x)的逆序对数,并且,对f(x)的操作即对b列的操作

//归并排序
#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 100000 + 10;
const int MOD = 99999997;
int f[MAXN],t[MAXN],cnt,n;
struct Node{
    int num, ord;
}a[MAXN], b[MAXN];
bool cmp(Node a, Node b) {
    return a.num < b.num;
}
void mergesort(int l, int r) {
    if(l == r) return;
    int mid = l+r>>1;
    mergesort(l,mid);
    mergesort(mid+1,r);
    int size = 0, tl = l, tr = mid+1;
    while(tl <= mid || tr <= r) {
        if(tr > r || tl<=mid && f[tl] < f[tr]) {
            t[++size] = f[tl++];
        } else {
            t[++size] = f[tr++];
            cnt = (cnt%MOD + mid - tl + 1) % MOD;
        }
    }
    for(int i=l; i<=r; i++) {
        f[i] = t[i-l+1];
    }
}
int main() {
    cin >> n;
    for(int i=1; i<=n; i++) {
        cin >> a[i].num;
        a[i].ord = i;
    }
    for(int i=1; i<=n; i++) {
        cin >> b[i].num;
        b[i].ord = i;
    }
    sort(a+1,a+n+1,cmp);
    sort(b+1,b+n+1,cmp);
    for(int i=1; i<=n; i++) {
        f[a[i].ord] = b[i].ord;
    }
    mergesort(1,n);
    cout << cnt % MOD;
    return 0;
}
//树状数组 
#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 100000 + 10;
const int MOD = 99999997;
int f[MAXN],t[MAXN],cnt,n,tree[MAXN];
struct Node{
    int num, ord;
}a[MAXN], b[MAXN];
bool cmp(Node a, Node b) {
    return a.num < b.num;
}
int lowbit(int x) {
    return x&(-x);
}
void update(int pos, int value) {
    while(pos <= n) {
        tree[pos] = (tree[pos] + value % MOD) % MOD;
        pos += lowbit(pos);
    }
}
int getsum(int pos) {
    int sum = 0;
    while(pos) {
        sum = (sum % MOD + tree[pos]) % MOD;
        pos -= lowbit(pos);
    }
    return sum;
}
int inverse(int pos) {
    return (pos - getsum(f[pos])) % MOD; // pos是位置,getsum求的是目前序列中比f[pos]小的数,做差就是逆序对数 
}
int main() {
    cin >> n;
    for(int i=1; i<=n; i++) {
        cin >> a[i].num;
        a[i].ord = i;
    }
    for(int i=1; i<=n; i++) {
        cin >> b[i].num;
        b[i].ord = i;
    }
    sort(a+1,a+n+1,cmp);
    sort(b+1,b+n+1,cmp);
    for(int i=1; i<=n; i++) {
        f[a[i].ord] = b[i].ord;
    }
    for(int i=1; i<=n; i++) {
        update(f[i], 1);
        cnt = (cnt % MOD + inverse(i)) % MOD;
    }
    cout << cnt % MOD;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值