TZOJ 9045 Pearls

描述

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数组为前缀和):

                                        b[r]-b[l]>=(r-l)*k

当l和r满足上述式子时,就说明l+1到r这一段的子序列是合法的。

再移项:

                        ​​​​​​​        ​​​​​​​        b[r]-r*k>=b[l]-l*k

再遍历l时, 右边的值是已知的。而要找出每个l所对应r的个数,只需把左边式子的值放在值域树里,就可以用logn的时间复杂度找到合法的r。

可以先把原数组所有值减去k,式子也会变简单:

                                         ​​​​​​​b[r]>=b[l]        

为了避免在找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();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值