HDU 5273 Dylans loves sequence——BestCoder Round #45(DP or 树状数组)
2015-08-10 17:00
435 查看
Dylans loves sequence
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Problem Description
Dylans is given N numbers a[1]....a[N]
And there are Q questions.
Each question is like this (L,R)
his goal is to find the “inversions” from number L to
number R.
more formally,his needs to find the numbers of pair(x,y),
that L≤x,y≤R and x<y and a[x]>a[y]
Input
In the first line there is two numbers N and Q.
Then in the second line there are N numbers:a[1]..a[N]
In the next Q lines,there
are two numbers L,R in
each line.
N≤1000,Q≤100000,L≤R,1≤a[i]≤231−1
Output
For each query,print the numbers of "inversions”
Sample Input
3 2 3 2 1 1 2 1 3
Sample Output
1 3
Hint
You shouldn't print any space in each end of the line in the hack data.
Source
BestCoder Round #45
/************************************************************************/
附上该题对应的中文题
Dylans loves sequence
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
问题描述
Dylans得到了NN个数a[1]...a a[1]...a[N]。 有QQ个问题,每个问题形如(L,R)(L,R) 他需要求出L-RL−R这些数中的逆序对个数。 更加正式地,他需要求出二元组(x,y)(x,y)的个数,使得L \leq x,y \leq RL≤x,y≤R且x < yx<y且a[x] > a[y]a[x]>a[y]
输入描述
第一行有两个数NN和QQ。 第二行给出NN个数字a[1]...a a[1]...a[N]。 接下来的QQ行,每行给出两个数L, RL,R。 N \leq 1000,Q \leq 100000,L \leq R,1 \leq a[i] \leq 2^{31}-1N≤1000,Q≤100000,L≤R,1≤a[i]≤231−1
输出描述
对于每个询问,输出逆序对个数。
输入样例
3 2 3 2 1 1 2 1 3
输出样例
1 3
Hint
hack数据里读入的每一行末尾不应该有多余的空格。
/****************************************************/
出题人的解题思路:
N只有10001000,于是想怎么来就怎么来。
最容易想到的是枚举开头,然后Nlog(N)Nlog(N)时间里去算逆序对,用一个树状数组维护。
(可惜BC不给卡。。。呜呜呜)
仔细一想发现可以很简单地做到N^{2}N2.
设ans[l][r]ans[l][r]为l
\sim rl∼r的逆序对数量。首先我们暴力地先算好ans[1][1..N]ans[1][1..N]。
然后ii从2
\sim N2∼N枚举,每次计算从ii开始的逆序对。
那么ans[i][j]ans[i][j]比ans[i-1][j]ans[i−1][j]少了什么呢?没错,少了a[i-1]a[i−1]这个数的贡献。
我们再开一个累加器cntcnt。枚举jj从i
\sim Ni∼N,如果a[i-1]a[i−1]和a[j]a[j]产生逆序对就cnt[j]=-1cnt[j]=−1 然后我们从右往左累加cntcnt(因为贡献是前缀和性质的)
最后ans[i][j]=ans[i-1][j]+cnt[j]ans[i][j]=ans[i−1][j]+cnt[j]。
预处理完所有的答案就可以O(1)O(1)的询问啦。
本题要我们求的就是区间[l,r]内逆序对的个数,首先,我们假设s[i][j]为i~j的逆序对的数量,这样我们可以初步算出s[i][i+1...N]中与i能形成逆序对的数量。然而,如果我们单纯只算这一步的话,会少算了点东西
举个例子
比如说我们通过[1,2]的逆序对数求出了[1,3]的逆序对数,当然这并不是完整的1~3范围的逆序对,它缺了2~3内的逆序对数
所以我们还得反向将结果整合一下,即先正向计算出[1,2]、[1,3]、[2,3],再反向将[2,3]整合到[1,3]中。方向的计算公式是s[j][i]+=s[j+1][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 ,a ; 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
,b
,c
,s
;
int lowbit(int t) //计算c[t]展开的项数
{
return t&(-t);
}
int Sum(int n) //求前n项的和.
{
int sum=0;
while(n>0)
{
sum+=c
;
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]);
}
}
}
菜鸟成长记
相关文章推荐
- 使用phpQuery 抓取HTML 页面内容
- easyui中的panel(面板)的属性href的使用
- zoj 3545 - Rescue the Rabbit(AC自动机+dp)
- POJ_1679_The Unique MST(次小生成树模板)
- codeforces 527D D. Clique Problem(二分+线段树+贪心+dp)
- 控件学习---UIImageView---摘自培训文档
- HDU5273.Dylans loves sequence(逆序数对)
- Integer Inquiry
- animate的{queue:false,duration:400}意思
- Django中的request.GET和request.POST
- Hdu oj 1005 Number Sequence
- UITableView 实现汽车品牌(demo)
- UIMenuController,UIPasteboard:复制,粘贴详细解释
- CodeForces 3D-Least Cost Bracket Sequence
- Flash Builder4.7破解方法
- 控件学习---UITextField---摘自培训资料
- MIUI 6 沉浸式状态栏(Android 4.4以上)
- UVA 1151 Buy or Build
- poj 1458/ hdu 1159 Common Subsequence
- Request的getParameter和getAttribute方法的区别