您的位置:首页 > 其它

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_ir​i​​表示[i,r_i][i,r​i​​]的逆序对数小于等于KK,且r_ir​i​​的值最大。显然r_ir​i​​单调不降,我们可以通过用两个指针扫一遍,利用树状数组计算出rr数组。

对于每个询问L,RL,R,我们要计算的是\sum_{i=L}^{R}[
\min(R, r_i)-i+1 ]∑​i=L​R​​[min(R,r​i​​)−i+1] 由于r_ir​i​​具有单调性,那我们直接在上面二分即可,然后记一个前缀和。总复杂度是\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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: