只能交换相邻两数得到相应序列 树状数组

有两个序列

对于序列b,每次只能交换相邻两个数

求将序列b变成序列a需要的最小交换数

套路都是一样的:把序列每个值对应一个升序的值,然后把b用a来转换,再把b变成1...n的升序的最小值,就是求逆序对的数量

来个例子:

a 1 4 2 3

b 3 2 4 1

把a中每个数与列数对应:

1 -> 1 4 -> 2 2 -> 3 3 -> 4

那么我们就可以把a看成 1 2 3 4

那么把b每个数用相同的转换规则转换 b : 4 3 2 1

把b变成a即把b变成升序,只能交换相邻两个数 ---> 逆序对数量

记得最坏情况是n * n级别的(n - 1) + (n - 2) + ... + 1 + 0, 有时候树状数组要开long long




这个题挺有意思的,有时候我们不需要知道转换的过程,我们只用关注结果对不对,以及不对的情况

AC代码:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define PII pair<int,int>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
#define rrep(i, n) for(int i = n; i >= 1; ++i)

using namespace std;

const double pi = acos(-1.0);

const int N = 2e6 + 10;
int a[2][N], b[2][N];
int vis[N], ans[N]; //vis要用2 * n来存
ll num[N]; //树状数组开long long
int n;

//数组下标范围:1 ~ n
int lowbit(int x){
	return x&(-x);
}

//添加
inline void update(int p){
	while (p <= n){
		++num[p];
		p += lowbit(p);
	}
}

//查询
inline ll query(int p){
	ll ans = 0;
	while (p > 0){
		ans += num[p];
		p -= lowbit(p);
	}
	return ans;
}

int main()
{
    scanf("%d", &n);
    rep(i, n) scanf("%d", &a[0][i]);
    rep(i, n) scanf("%d", &a[1][i]);
    rep(i, n) scanf("%d", &b[0][i]);
    rep(i, n) scanf("%d", &b[1][i]);
    rep(i, n){ //把偶数列交换之后,那么不管怎么变,每一列上下顺序一定都是这样的
        if(i % 2 == 0) swap(a[0][i], a[1][i]), swap(b[0][i], b[1][i]);
        vis[a[0][i]] = a[1][i];
    }
    rep(i, n) if(b[1][i] != vis[b[0][i]]){ printf("dldsgay!!1"); return 0;}
    rep(i, n) vis[a[0][i]] = i;
    rep(i, n) ans[i] = vis[b[0][i]];
    ll cnt = 0;
    rep(i, n)
    {
        cnt += i - 1 - query(ans[i]);
        update(ans[i]);
    }
    printf("%lld", cnt);
    return 0;
}

[NOIP2013 提高组] 火柴排队 - 洛谷

这个题难度高一点,可能是要证明结论和离散化吧  

AC代码:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define PII pair<int,int>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
#define rrep(i, n) for(int i = n; i >= 1; ++i)

using namespace std;

const double pi = acos(-1.0);

const int N = 1e5 + 10;
const int M = 1e8 - 3;
PII a[N], b[N];
int c[N], d[N], ans[N];
ll num[N];
int n;


int lowbit(int x){
	return x&(-x);
}

//添加
inline void update(int p){
	while (p <= n){
		num[p] = (num[p] + 1) % M;
		p += lowbit(p);
	}
}

//查询
inline ll query(int p){
	ll ans = 0;
	while (p > 0){
		ans = (ans + num[p]) % M;
		p -= lowbit(p);
	}
	return ans;
}

int main()
{
    scanf("%d", &n);
    rep(i, n) scanf("%d", &a[i].first), a[i].second = i;
    rep(i, n) scanf("%d", &b[i].first), b[i].second = i;
    sort(a + 1, a + 1 + n);
    sort(b + 1, b + 1 + n);
    rep(i, n) c[a[i].second] = i, d[b[i].second] = i;
    rep(i, n) ans[c[i]] = i;
    ll res = 0;
    rep(i, n)
    {
        res = (res + i - 1 - query(ans[d[i]])) % M;
        update(ans[d[i]]);
    }
    printf("%lld", res);
    return 0;
}

写到类似题继续补充~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值