3 2 3 2 1 1 2 1 3
1 3HintYou shouldn't print any space in each end of the line in the hack data.
附上该题对应的中文题
Dylans得到了N个数a[1]...a[N]。 有Q个问题,每个问题形如(L,R) 他需要求出L−R这些数中的逆序对个数。 更加正式地,他需要求出二元组(x,y)的个数,使得L≤x,y≤R且x<y且a[x]>a[y]
第一行有两个数N和Q。 第二行给出N个数字a[1]...a[N]。 接下来的Q行,每行给出两个数L,R。 N≤1000,Q≤100000,L≤R,1≤a[i]≤231−1
对于每个询问,输出逆序对个数。
3 2 3 2 1 1 2 1 3
1 3
hack数据里读入的每一行末尾不应该有多余的空格。
出题人的解题思路:
N只有1000,于是想怎么来就怎么来。
最容易想到的是枚举开头,然后Nlog(N)时间里去算逆序对,用一个树状数组维护。 (可惜BC不给卡。。。呜呜呜)
仔细一想发现可以很简单地做到N2.
设ans[l][r]为l∼r的逆序对数量。首先我们暴力地先算好ans[1][1..N]。 然后i从2∼N枚举,每次计算从i开始的逆序对。
那么ans[i][j]比ans[i−1][j]少了什么呢?没错,少了a[i−1]这个数的贡献。 我们再开一个累加器cnt。枚举j从i∼N,如果a[i−1]和a[j]产生逆序对就cnt[j]=−1 然后我们从右往左累加cnt(因为贡献是前缀和性质的)
最后ans[i][j]=ans[i−1][j]+cnt[j]。 预处理完所有的答案就可以O(1)的询问啦。
本题要我们求的就是区间[l,r]内逆序对的个数,首先,我们假设s[i][j]为i~j的逆序对的数量,这样我们可以初步算出s[i][i+1...N]中与i能形成逆序对的数量。然而,如果我们单纯只算这一步的话,会少算了点东西貌似涉及到这方面的题目,讲起来总是有点混乱,不过大家可以多多交流,这样能够理解得更透彻
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<queue> #include<math.h> #include<vector> #include<map> #include<set> #include<stdlib.h> #include<cmath> #include<string> #include<algorithm> #include<iostream> #define exp 1e-10 using namespace std; const int N = 1005; const int inf = 1000000000; int s[N][N],a[N]; int main() { int n,q,i,j,k,l,r; while(~scanf("%d%d",&n,&q)) { for(i=1;i<=n;i++) scanf("%d",&a[i]); memset(s,0,sizeof(s)); for(i=1;i<=n;i++) for(j=i+1;j<=n;j++) { s[i][j]+=s[i][j-1]; if(a[j]<a[i]) s[i][j]++; } for(i=n;i>0;i--) for(j=i-1;j>0;j--) s[j][i]+=s[j+1][i]; for(i=0;i<q;i++) { scanf("%d%d",&l,&r); printf("%d\n",s[l][r]); } } return 0; }
另一种方法就是用树状数组来进行预处理(虽然此题用树状数组稍显麻烦)
因为区间[l,r]之间的逆序对个数s[l][r]=区间[l,r-1]之间的逆序对个数+区间[l,r-1]加入第r个数a[r]所增加的逆序对个数
区间[l,r-1]之间的逆序对个数是已知的,关键就在于怎么用树状数组来求区间[l,r-1]加入第r个数a[r]所增加的逆序对个数
现在可以这么思考,我要想知道加入a[r]之后,增加了多少的逆序对,只要知道区间[l,r-1]内有多少比a[r]来得大的数即可,而这一步就可以用到树状数组。
在计算a[r]时,前面的a[l]到a[r-1]都已经对树状数组进行更新
比如说3 2 1,计算[1,2]时,[1,1]已经求出,且3已经对树状数组进行更新,当你加入第2个数2时,树状数组中比2大的数有1个,即3,树状数组总的和-小于等于2的数的个数就是大于2的数的个数,那就是加入2之后增加的逆序对个数
可能举例之后你还是不太明白我到底在说什么,但是不要紧,有问题可以提,我会尽快予以解答
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<queue> #include<math.h> #include<vector> #include<map> #include<set> #include<stdlib.h> #include<cmath> #include<string> #include<algorithm> #include<iostream> #define exp 1e-10 using namespace std; const int N = 1005; const int inf = 1000000000; int a[N],b[N],c[N],s[N][N]; int lowbit(int t) //计算c[t]展开的项数 { return t&(-t); } int Sum(int n) //求前n项的和. { int sum=0; while(n>0) { sum+=c[n]; n-=lowbit(n); } return sum; } void update(int i) { while(i<=b[0]) { c[i]++; i+=lowbit(i); } } int main() { int i,j,m,n,p,q,l,r; while(~scanf("%d%d",&n,&q)) { for(i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i]; sort(b+1,b+n+1);//将数组b排序 b[0]=unique(b+1,b+n+1)-(b+1);//去除数组b中相邻的重复元素 for(i=1;i<=n;i++) a[i]=lower_bound(b+1,b+b[0]+1,a[i])-b; //姑且可以叫做数据压缩吧,因为只要考虑大小关系,所以1 5 8完全可以记为1 2 3,减小树状数组的整体大小 for(i=1;i<=n;i++) { memset(c,0,sizeof(c)); for(j=i;j<=n;j++) { s[i][j]=s[i][j-1]+Sum(b[0])-Sum(a[j]); update(a[j]); } } while(q--) { scanf("%d%d",&l,&r); printf("%d\n",s[l][r]); } } }
菜鸟成长记