51nod1685:第K大区间2

1685 第K大区间2
基准时间限制:1.5 秒 空间限制:131072 KB 分值: 160  难度:6级算法题
 收藏
 关注

定义一个长度为奇数的区间的值为其所包含的的元素的中位数。中位数_百度百科 

现给出n个数,求将所有长度为奇数的区间的值排序后,第K大的值为多少。


样例解释:


[l,r]表示区间的值
[1]:3
[2]:1
[3]:2
[4]:4
[1,3]:2
[2,4]:2


第三大是2

Input
第一行两个数n和k(1<=n<=100000,k<=奇数区间的数量)
第二行n个数,0<=每个数<2^31
Output
一个数表示答案。
Input示例
4 3
3 1 2 4
Output示例
2

二分答案t,统计中位数大于等于t的区间有多少个。
设a[i]为前i个数中有a[i]个数>=t,若奇数区间[l,r]的中位数>=t,则(a[r]-a[l-1])*2>r-l+1,即(a[r]*2-r)>(a[l-1]*2-l+1)。
设b[i]=a[i]*2-i,统计每个b[i]有多少个b[j] 总复杂度O(nlognlogn)


卡在统计上了,想用multiset二分发现不成,树状数组记录前面b[x]的值,直接统计。

代码:

//#pragma comment(linker, "/STACK:102400000,102400000") 
#pragma warning(disable:4996)
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;

#define LL_INF 0x3fffffffffffffff
#define INT_INF 0x3fffffff
#define mem(a, b) memset(a, b, sizeof(a))
#define pper(i,n,m) for(int i = n;i >= m; i--)
#define repp(i, n, m) for (int i = n; i <= m; i++)
#define rep(i, n, m) for (int i = n; i < m; i++)
#define sa(n) scanf("%d", &(n))
#define mp make_pair
#define ff first
#define ss second
#define pb push_back

const int maxn = 4 * 200005;
const ll mod = 1e9 + 7;
const double PI = acos(-1.0);

ll n, k;
ll val[maxn], b[maxn];

ll d[2][maxn];

int lowbit(int x)
{
	return x&(-x);
}

void add(int pos, int x, int v)
{
	while (x <= maxn)
	{
		d[pos][x] += v;
		x += lowbit(x);
	}
}

ll get(int pos, int x)
{
	ll res = 0;
	while (x > 0)
	{
		res += d[pos][x];
		x -= lowbit(x);
	}
	return res;
}

bool check(ll x)
{
	ll i, j;
	mem(b, 0);
	for (i = 1; i <= n; i++)
	{
		if (val[i] >= x)
		{
			b[i] = 1 + b[i - 1];
		}
		else
		{
			b[i] = b[i - 1];
		}
	}
	for (i = 1; i <= n; i++)
	{
		b[i] = b[i] * 2 - i;
	}
	mem(d, 0);
	ll res = 0, sum = 0;
	add(0, 2 * n, 1);
	for (i = 1; i <= n; i++)
	{
		res += get(!(i & 1), 2 * n + b[i] - 1);
		add(i & 1, 2 * n + b[i], 1);
	}
	return res >= k;
}

void solve()
{
	ll i, j;
	scanf("%lld%lld", &n, &k);

	for (i = 1; i <= n; i++)
	{
		scanf("%lld", &val[i]);
		b[i] = val[i] * 2 - i;
	}
	ll le = 0, ri = INT_INF;
	ll mid;
	while (le < ri)
	{
		mid = (le + ri + 1) / 2;
		if (check(mid))
		{
			le = mid;
		}
		else
		{
			ri = mid - 1;
		}
	}
	printf("%lld", le);
}

int main()
{
	solve();
	return 0;
}



你可能感兴趣的:(数据结构-线段树与树状数组)