hdu 5196 DZY Loves Inversions(树状数组,二分法,逆序数)
2016-07-25 17:09
375 查看
DZY Loves Inversions
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 433 Accepted Submission(s): 114
Problem Description
DZY has a array a,
consisting of n positive
integers, indexed from 1 to n.
Let's denote the number with index i as ai.
DZY wants to count, with a given pair (l,r)(l≤r),
how many pairs of integers i and j are
there, such that l≤i≤j≤r and
the sequence b=aiai+1⋯aj has
exactly k inversions.
Moreover, DZY has q queries.
Input
The input consists several test cases.(TestCase≤5)
The first line contains three integers n,q,k(1≤n≤105,1≤q≤105,0≤k≤1018).
The next line contains n positive
integers, separated by single spaces, a1,a2,⋯,an(1≤ai≤109).
Each of the next q lines
has two integers: l,r,
representing a query.
Output
For each query, please print a line containing the answer.
Sample Input
6 4 1
3 1 5 4 2 6
2 4
2 3
3 4
1 5
Sample Output
2
0
1
5
Hint1query 1. (2,4), (3,4) are ok.
query 2. No such pair.
query 3. (3,4) is ok.
query 4. (1,2), (1,3), (2,4), (3,4), (4,5) are ok.
题意:给定序列,求区间[l,r]中有多少个逆序数为k的子区间
思路:看了别人代码都做了好久- - ....菜的抠脚。
思路跟官方题解是一样的
考虑如何计算一个区间中有多少个子区间的逆序对数小于等于KK。这样做两遍就能算出恰好等于KK的了。
对于i(1\leq i \leq n)i(1≤i≤n),我们计算r_iri表示[i,r_i][i,ri]的逆序对数小于等于KK,且r_iri的值最大。显然r_iri单调不降,我们可以通过用两个指针扫一遍,利用树状数组计算出rr数组。
对于每个询问L,RL,R,我们要计算的是\sum_{i=L}^{R}[
\min(R, r_i)-i+1 ]∑i=LR[min(R,ri)−i+1] 由于r_iri具有单调性,那我们直接在上面二分即可,然后记一个前缀和。总复杂度是\text{O}((n+q)
\log n)O((n+q)logn)。离线的话的还可以做到\text{O}(n
\log n + q)O(nlogn+q),都是非常优秀的。
首先我们用把原数组离散化,然后求出在逆序数<=k的条件下以i点为区间的左端点最多可以延伸到哪个点(右端点记为far[i])
显然far数组是单调不降的(左端点右移的话,逆序数要么不变要么减少,所以far[i]>=far[i-1])
然后利用树状数组求逆序数的模型我们可以求出far数组
然后难点来了。 在有far数组的情况下我们如何在O(logn)的时间复杂度内解决每一个区间询问
首先我们维护前缀和,qian[i]保存左端点>=i点的所有区间的逆序数<=k的数量
然后每次二分找出第一个far[i]>=右端点的点
进行求和即可
代码:#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define N 100050
long long c
,a
,l
,r
,far
,sum2
;
long long ans
,qian
,sum1
;
int n,m;
struct Node
{
int id,v;
} p
;
int lowbit(int x)
{
return x&-x;
}
void add(long long x,int v)
{
for(int i=x; i<=n; i+=lowbit(i))
c[i]+=v;
}
long long get(int x)
{
long long ans=0;
for(int i=x; i; i-=lowbit(i))
ans+=c[i];
return ans;
}
bool cmp(Node a,Node b)
{
return a.v<b.v;
}
void solve(long long k,int v)
{
if(k<0) return;
memset(c,0,sizeof(c));
long long sum=0;
int rr=0;
for(int i=1; i<=n; i++)
{
while(rr<=n&&sum<=k)
{
rr++;
if(rr>n) break;
sum+=(rr-i)-get(a[rr]);///新加进r点的逆序数等于r点左边比a[r]大的数
add(a[rr],1);///添加a[i]点
}
far[i]=rr-1;
add(a[i],-1);///将a[i]点删除
sum-=get(a[i]-1);///现在树状数组里面<=a[i]的数的个数,即在i点右边且比i点小的数的个数
}
qian[0]=0;
for(int i=1;i<=n;i++)
qian[i]=qian[i-1]+(far[i]-i+1);
for(int i=1;i<=m;i++)
{
int x=l[i],y=r[i];
int pos=lower_bound(far+x,far+y+1,y)-far-1;
ans[i]+=v*(qian[pos]-qian[x-1]+sum2[y-pos]);
}
}
int main()
{
long long k;
sum2[0]=0;
for (int i=1;i<N;i++)
sum2[i] = sum2[i - 1] + i;
while(~scanf("%d %d %lld",&n,&m,&k))
{
for(int i=1; i<=n; i++)
{
scanf("%lld",&p[i].v);
p[i].id=i;
}
sort(p+1,p+1+n,cmp);
int cnt=1;
a[p[1].id]=1;
for(int i=2; i<=n; i++)
{
if(p[i].v!=p[i-1].v) cnt++;
a[p[i].id]=cnt;
}
for(int i=1; i<=m; i++)
scanf("%lld %lld",&l[i],&r[i]);
memset(ans,0,sizeof(ans));
solve(k,1);
solve(k-1,-1);
for(int i=1; i<=m; i++)
printf("%lld\n",ans[i]);
}
return 0;
}
相关文章推荐
- 学习笔记-mysql函数
- 三千词汇
- 本类的静态指针
- 键盘快捷键
- 4000 dede开发-from表单提交后返回当前界面
- loss=nan解决办法
- VB程序学习代码记录20160725
- linux使用 java -jar来运行java非web程序
- Oracle 高级分组
- java中的匿名内部类总结
- Session 未将对象引用到实例
- 如何在MenuItem中添加icon图标
- Android Otto事件总线结合Fragment与AsyncTask的分享
- just believe yourself
- UML类图与类的关系
- java有序数组
- java基础-继承
- HTML 颜色速查表
- 如何让一个div显示在页面最顶层
- 计算几何相关公式