题目地址:https://www.luogu.org/problem/P1774
题意:
求将一个序列变成下降的序列,所需要的最少交换次数(转化成求逆序对问题+离散化+long long)
ac代码:
树状数组法:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 500015;
#define lowbit(x) ((x)&(-x))
int n;
long long c[maxn], a[maxn], b[maxn];
long long res;
void update(int x, int v)
{
for(int i = x; i <= n; i += lowbit(i))
c[i] += v;
}
long long getsum(int x)
{
long long ans = 0;
for(int i = x; i > 0; i -= lowbit(i))
ans += c[i];
return ans;
}
int main()
{
//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
b[i] = a[i];
}
sort(a+1, a+1+n);
int num = unique(a+1, a+1+n) - (a+1);
for(int i = n; i >= 1; i--) //倒序处理,寻找后面比它小的数的个数,因为getsum是求的1-x的和
{
int no = lower_bound(a+1, a+1+num, b[i]) - a;
res += getsum(no - 1); //在它后边比它小的
update(no, 1);
}
printf("%lld\n", res);
return 0;
}
权值线段树法:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 500010;
long long a[maxn], b[maxn], cnt[4*maxn];
long long res;
void update(int id )
{
cnt[id] = cnt[id << 1] + cnt[id << 1 | 1];
}
void build(int l, int r, int id, int no)
{
if(l == r)
{
cnt[id]++;
return ;
}
int mid = (l + r) >> 1;
if(no <= mid) build(l, mid, id << 1, no);// 单点
else build(mid + 1, r, id << 1 | 1, no);
update(id);
}
long long query(int l, int r, int id, int k) //查询目前为止第k小到最大的数有多少个
{
if(l == r)
{
//printf("l:%d-r:%d sum: %lld\n", l, r, cnt[id]);
return cnt[id]; //第l小的数目前为止出现了cnt[id]次
}
int mid = (l + r) >> 1;
long long sum = 0;
if(k <= mid) sum += query(l, mid, id << 1, k) + cnt[id << 1 | 1]; //目标是要找到l == r =k
else sum += query(mid + 1, r, id << 1 | 1, k);//!k - cnt[id << 1]
// printf("l:%d-r:%d sum: %lld\n", l, r, sum);
return sum;
}
int main()
{
//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
b[i] = a[i];
}
sort(a+1, a+1+n);
int num = unique(a+1, a+1+n) - (a+1);
for(int i = 1; i <= n; i++)
{
int no = lower_bound(a+1, a+1+num, b[i]) - a;
// printf("b[i]:%d no:%d \n ", b[i], no);
long long que = query(1, num+1, 1, no+1); //找到第no+1小到第num+1小的数出现了多少次,即比no大的数出现了多少次
//printf("que:%lld\n", que);
res += que;
build(1, num+1, 1, no);
}
printf("%lld\n", res);
return 0;
}