描述
There are n pearls strung together in a line, each pearl has a value ai. You can choose several consecutive pearls from them, and the average value of the selected pearls should not be lower than k. How many ways are there in total.
输入
The first line includes two integers n (1≤n≤2×105) and k (1≤k≤109) .
The second line includes a1, a2, …, an (1≤ai≤109) .
输出
Print the number of ways that meet the requirements.
题意
给定一段序列,求出其中平均数大于k的公共子序列个数。
大致思路
通过遍历所有的左端点,再快速的找出相对应的右端点,就能以nlogn的时间复杂度解决题目。先写数学公式(b数组为前缀和):
当l和r满足上述式子时,就说明l+1到r这一段的子序列是合法的。
再移项:
再遍历l时, 右边的值是已知的。而要找出每个l所对应r的个数,只需把左边式子的值放在值域树里,就可以用logn的时间复杂度找到合法的r。
可以先把原数组所有值减去k,式子也会变简单:
为了避免在找r时,r的位置在l左边这种情况。可以从后往前遍历,一边插入r对应的值,一边查找。
题中的值域比较大,可以动态开点或者离散化。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void IOS()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
}
struct Node
{
ll cc, lp, rp;
}tree[10000007];
ll a[200007], n, k, b[200007], cnt = 1, root, minn = 1e18, maxn = -1e18;
void insert(ll l, ll r, ll& p, ll q)
{
if (!p)p = cnt++;
tree[p].cc += 1;
if (l == r)return;
ll mid = l + r >> 1;
if (q <= mid)insert(l, mid, tree[p].lp, q);
else insert(mid + 1, r, tree[p].rp, q);
}
ll query(ll l, ll r, ll p, ll q)
{
//printf("%lld %lld %lld %lld %lld\n",l,r,p,q,tree[p].cc);
if (p == 0)return 0;
if (l == r)
{
if (q <= l)return tree[p].cc;
else return 0;
}
ll mid = l + r >> 1;
ll lp = tree[p].lp, rp = tree[p].rp;
if (q <= mid)return query(l, mid, lp, q) + tree[rp].cc;
else return query(mid + 1, r, rp, q);
}
void solve()
{
cin >> n >> k;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
a[i] -= k;
b[i] = b[i - 1] + a[i];
minn = min(b[i], minn);
maxn = max(b[i], maxn);
}
ll ans = 0;
for (int i = n; i >= 1; i--)
{
insert(minn, maxn, root, b[i]);
ans += query(minn, maxn, root, b[i - 1]);
}
cout << ans;
}
int main()
{
IOS();
int T = 1;
//cin>>T;
while (T--)
{
solve();
}
}