1.逆序数:
求逆序数
-
描述
-
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。
现在,给你一个N个元素的序列,请你判断出它的逆序数是多少。
比如 1 3 2 的逆序数就是1。
-
输入
-
第一行输入一个整数T表示测试数据的组数(1<=T<=5)
每组测试数据的每一行是一个整数N表示数列中共有N个元素(2〈=N〈=1000000)
随后的一行共有N个整数Ai(0<=Ai<1000000000),表示数列中的所有元素。
数据保证在多组测试数据中,多于10万个数的测试数据最多只有一组。
输出
- 输出该数列的逆序数 样例输入
-
2 2 1 1 3 1 3 2
样例输出
-
0 1
-
第一行输入一个整数T表示测试数据的组数(1<=T<=5)
算法1:
归并:
逆序数为sum
l [n]和r [m]为已经排好的
则如果l [i] > r [j]
则sum+=(n-i);
#include <iostream>
#include <string.h>
using namespace std;
#define maxn 1000010
int s[maxn];
int ll[maxn/2],rr[maxn/2];
int sum=0;
void mg(int l,int m,int r){
int i,j,k=0;
for(i=l;i<=m;i++){
ll[k++]=s[i];
}k=0;
for(i=m+1;i<=r;i++){
rr[k++]=s[i];
}
int a,b;
a=m-l+1;b=r-m;
k=l;
for(i=0,j=0;i<a && j<b;){
if(ll[i]<=rr[j]){
s[k++]=ll[i];i++;
}else{
s[k++]=rr[j];j++;
sum+=(a-i);
}
}
while(i<a){
s[k++]=ll[i++];
}
while(j<b){
s[k++]=rr[j++];
}
}
void mf(int l,int r){
if(l<r){
int m=(l+r)/2;
mf(l,m);
mf(m+1,r);
mg(l,m,r);
}
}
int main(){
int nc;
cin>>nc;
while(nc--){
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>s[i];
}
mf(0,n-1);
cout<<sum<<endl;
}
return 0;}
算法2:
树状数组:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 1000000
long long c[N+1],aaa[N+1];
struct sb
{
int x;
int y;
}yi[N+1];
bool cmp(struct sb t1,struct sb t2)
{
if(t1.x!=t2.x)
return t1.x<t2.x;
else
return t1.y<t2.y;
}
int lowbit (int a)
{
return a&(-a);
}
void add(int a)
{
while(a<N+1)
{
c[a]+=1;
a+=lowbit(a);
}
}
int Sum(int a)
{
int SUM=0;
while(a>0)
{
SUM+=c[a];
a-=lowbit(a);
}
return SUM;
}
int main()
{
int a,b,n,m;
scanf("%d",&n);
while(n--)
{
long long sum=0;
memset(c,0,sizeof(c));
scanf("%d",&m);
for(a=1;a<=m;a++)
{
scanf("%lld",&yi[a].x);
yi[a].y=a;
}
std::sort(yi+1,yi+m+1,cmp);//离散化。。。
for(a=1;a<=m;a++)
aaa[yi[a].y]=a;
for(a=1;a<=m;a++)
{
add(aaa[a]);
sum+=(a-1-Sum(aaa[a]-1));
}
printf("%lld\n",sum);
}
}
Alice是一个让人非常愉跃的人!他总是去学习一些他不懂的问题,然后再想出许多稀奇古怪的题目。这几天,Alice又沉浸在逆序对的快乐当中,他已近学会了如何求逆序对对数,动态维护逆序对对数等等题目,他认为把这些题让你做简直是太没追求了,于是,经过一天的思考和完善,Alice终于拿出了一道他认为差不多的题目:
有一颗2n-1个节点的二叉树,它有恰好n个叶子节点,每个节点上写了一个整数。如果将这棵树的所有叶子节点上的数从左到右写下来,便得到一个序列a[1]…a[n]。现在想让这个序列中的逆序对数量最少,但唯一的操作就是选树上一个非叶子节点,将它的左右两颗子树交换。他可以做任意多次这个操作。求在最优方案下,该序列的逆序对数最少有多少。
Alice自己已近想出了题目的正解,他打算拿来和你分享,他要求你在最短的时间内完成。
第一行一个整数n。
下面每行,一个数x。
如果x=0,表示这个节点非叶子节点,递归地向下读入其左孩子和右孩子的信息,如果x≠0,表示这个节点是叶子节点,权值为x。
0
0
3
1
2
对于20%的数据,n <= 5000。
对于100%的数据,1 <= n <= 200000,0 <= a[i]<2^31。
代码:--摘自网络
#include<stdio.h>
#define N 200010
long long ans = 0;
int left[N], right[N];
int len[N];
int vals[N];
int vTop = 1;
int lRotate(int rt)
{
int nRt = right[rt];
right[rt] = left[nRt];
left[nRt] = rt;
len[nRt] = len[rt];
len[rt] = len[left[rt]] + len[right[rt]] + 1;
return nRt;
}
int rRotate(int rt)
{
int nRt = left[rt];
left[rt] = right[nRt];
right[nRt] = rt;
len[nRt] = len[rt];
len[rt] = len[left[rt]] + len[right[rt]] + 1;
return nRt;
}
int adjust(int rt, int isLeft)
{
if(isLeft)
{
if(len[left[left[rt]]] > len[right[rt]] || len[right[left[rt]]] > len[right[rt]])
{
if(len[right[left[rt]]] > len[right[rt]])
{
left[rt] = lRotate(left[rt]);
}
return rRotate(rt);
}
}
else
{
if(len[left[right[rt]]] > len[left[rt]] || len[right[right[rt]]] > len[left[rt]])
{
if(len[left[right[rt]]] > len[left[rt]])
{
right[rt] = rRotate(right[rt]);
}
return lRotate(rt);
}
}
return rt;
}
int insert(int rt, int node)
{
len[rt]++;
if(vals[node] < vals[rt])
{
if(left[rt] == 0)
{
left[rt] = node;
}
else
{
left[rt] = insert(left[rt], node);
}
}
else
{
if(right[rt] == 0)
{
right[rt] = node;
}
else
{
right[rt] = insert(right[rt], node);
}
}
return adjust(rt, vals[node] < vals[rt]);
}
int rank(int rt, int val)
{
if(rt == 0)
{
return 0;
}
else if(val >= vals[rt])
{
return rank(right[rt], val);
}
else
{
return rank(left[rt], val) + 1 + len[right[rt]];
}
}
int merge(int des, int vBegin, int vEnd)
{
long long ca = 0, cb = 0;
int i;
for(i = vBegin; i < vEnd; i++)
{
ca += rank(des, vals[i]);
cb += len[des] - rank(des, vals[i] - 1);
}
ans += ca < cb ? ca : cb;
for(i = vBegin; i < vEnd; i++)
{
left[i] = right[i] = 0;
len[i] = 1;
des = insert(des, i);
}
return des;
}
int buildTree()
{
int val;
scanf("%d", &val);
if(val != 0)
{
left[vTop] = right[vTop] = 0;
len[vTop] = 1;
vals[vTop] = val;
return vTop++;
}
int ls = vTop;
int rlt = buildTree();
int rs = vTop;
int rrt = buildTree();
int re = vTop;
if(rs - ls > re - rs)
{
return merge(rlt, rs, re);
}
else
{
return merge(rrt, ls, rs);
}
}
int main(void)
{
int n;
scanf("%d", &n);
buildTree();
printf("%I64d", ans);
return 0;
}