拖了很久今天终于下定决心来解决这道题了,这么经典的题目居然没有人写题解,本小白就来水一发(大佬勿喷),
希望能对大家有点帮助,主要讲下思想和两种方法的实现,感觉难点问题官方题解已经讲的很清楚了。
归并排序解法:
首先我们来看下为什么归并排序可以计算逆序对对数量:
比如一个数列为 5 4 1 2 3
首先在归并排序将数列分为两部分 5 4 1 和 2 3
左边 5 4 1再次分成两部分 5 4 和 1
然后5 4再被分为两部分,5和4
接着便开始逐步合并(重点来了):在合并的时候,由于5 排在 4前面,由于是从小到打排序,必然要先放置4,再放置5,这个时候我们便可以将逆序对+1了,该部分合并完成后变为 4 5,接着与 1合并,由于1比4,5都小,还排在4,5后面,这个时候要把1提前,变为1,4,5这样逆序对就+2,接下来的合并相同,这样排序完成便可以计算出逆序对的个数了。
注:这里只是思想,重点是代码!!!
#include<bits/stdc++.h>
using namespace std;
int a[100000],YHT[100086];//a数组为合并时候需要用于暂存的数组,YHT为初始数据存储的数组
int ans=0,l,r;
inline int read(){ //快读模板
int ans = 0, f = 1;
char ch = getchar();
if (ch == '-')
f *= -1;
while (ch < '0' || ch > '9') {
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
ans = ans * 10 + ch - '0';
ch = getchar();
}
return ans * f;
}
void mergesort(int l,int r)//归并排序
{
if(l==r) return;//先判断递归结束条件,当区间段只有一个元素的时候便可以结束
int mid=(l+r)>>1;// (l+r)/2与(l+r)>>1等同,但是后者更快,详情大家可以看看百度看看位运算;
if(l<=mid) mergesort(l,mid); //左递归
if(mid+1<=r) mergesort(mid+1,r);//右递归
int ln=l,rn=mid+1,k=l; //合并操作 比如l-mid 为 1 3 4 mid+1-r为 2 6 7 一开始ln指向左部第一个元素即1,rn指向右部第一个元素即2,k用于存储的时候记录位置,一开始指向该区段最左边位置
while(ln<=mid&&rn<=r)
{
if(YHT[ln]<YHT[rn]) a[k++]=YHT[ln++];
else
{
a[k++]=YHT[rn++];//右部的放到前面来
ans+=mid-ln+1;//mid-ln+1为还没有放置的左部元素,即为和这部操作放置元素组成逆序对的元素
}
}
while(ln<=mid) a[k++]=YHT[ln++];
while(rn<=r) a[k++]=YHT[rn++];
for(int i=l;i<=r;i++)//将暂存数组中的数据放入主数组中
{
YHT[i]=a[i];
}
}
int main()
{
int n,m;
n=read();
for(int i=0;i<n;i++)
{
YHT[i]=read();
}
mergesort(0,n-1);
m=read();
ans%=2;//判断逆序对个数的奇偶性
while(m--)
{
l=read();
r=read();
int p=(r-l+1)*(r-l)/2;
if(p&1) ans^=1;//p&1与p%2==1等同,因为只有p是奇数才可以能对逆序对的奇偶性产生影响,因为ans非0,即1,相互转化用异或即可
if(ans) printf("dislike\n");
else printf("like\n");
}
}
2.树状数组
树状数组能快速求出前缀和,这里可以讲前缀和看作是逆序对的数列,还是举个例子吧
元素:5 4 1 3 2
下标:1 2 3 4 5
我们可以讲数组从大到小排序,下标不变:
排序后为:
元素 5 4 3 2 1
下表 1 2 4 5 3
这样就能保证先加入比后加入的大
初始状态树状数组为0 0 0 0 0
先加入5,5对应的下标为1,树状数组变为1 0 0 0 0
接着加入4,4对应的下标是2,树状数组变为 1 1 0 0 0
这时我们发现2前面已经有加入的,这样便构成一个逆序对,需要加1
接着加入3,3对应的下标为4,树状数组变为 1 1 0 1 0
4坐标前的前缀和为2(1+1),所以这样又有两个逆序对
每次都这样,最后便可以求出逆序对个数,因为树状数组可以用于快速求前缀和,便可以使用他来加快速度。
注:这只是思想,希望大家能去学习树状数组的写法,确实是个好东西。由于本人也刚起步,理解也只有这种水平了。
#include<bits/stdc++.h>
using namespace std;
struct Node
{
int num;//位置下标
int val;//值
};
Node a[100086];
int YHT[100086];
int n,m,ans=0,l,r;
inline int read() {
int ans = 0, f = 1;
char ch = getchar();
if (ch == '-')
f *= -1;
while (ch < '0' || ch > '9') {
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
ans = ans * 10 + ch - '0';
ch = getchar();
}
return ans * f;
}
int lowbit(int x)//树状数组模板
{
return x&(-x);
}
void update(int x)
{
for(int i=x;i<=n;i+=lowbit(i))
{
YHT[i]+=1;
}
}
int query(int x)
{
int sum=0;
for(int i=x;i>0;i-=lowbit(i))
{
sum+=YHT[i];
}
return sum;
}
bool cmp(Node a,Node b)//定义排序方法
{
return a.val>b.val;
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
{
a[i].val=read();
a[i].num=i;
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
ans+=query(a[i].num);//求前缀和
update(a[i].num);//更新
}
ans%=2;
m=read();
while(m--)
{
l=read();
r=read();
int p=(r-l+1)*(r-l)/2;
if(p&1) ans^=1;
if(ans) printf("dislike\n");
else printf("like\n");
}
} 小白写题解不易,大家如果觉得有帮助的话,点个赞哦,祝大家天天AC。

京公网安备 11010502036488号