1. 程式人生 > >Luogu P2717 寒假作業(平衡樹)

Luogu P2717 寒假作業(平衡樹)

P2717 寒假作業

題意

題目背景

\(zzs\)\(zzy\)正在被寒假作業折磨,然而他們有答案可以抄啊。

題目描述

他們共有\(n\)項寒假作業。\(zzy\)給每項寒假作業都定義了一個疲勞值\(A_i\),表示抄這個作業所要花的精力。\(zzs\)現在想要知道,有多少組連續的寒假作業的疲勞值的平均值不小於\(k\)

簡單地說,給定\(n\)個正整數\(A_1,A_2,A_3,\dots ,A_n\),求出有多少個連續的子序列的平均值不小於\(k\)

輸入輸出格式

輸入格式:

第一行兩個正整數,\(n\)\(k\)

第二行到第\(n+1\)行,每行一個正整數\(A_i\)

輸出格式:

一個非負整數。

輸入輸出樣例

輸入樣例#1:

3 2
1
2
3

輸出樣例#1:

4

說明

樣例解釋:共有\(6\)個連續的子序列,分別是\((1),(2),(3),(1,2),(2,3),(1,2,3)\),平均值分別為\(1,2,3,1.5,2.5,2\),其中平均值不小於\(k\)的共有\(4\)個。

對於\(20\%\)的資料,\(1\leq n\leq 100\)

對於\(50\%\)的資料,\(1\leq n\leq 5000\)

對於\(100\%\)的資料,\(1\leq n\leq 100000\)

對於\(100\%\)的資料,\(1\leq A_i\leq 10000,1\leq k\leq 10000\)

思路

統計一個字首和\(s\),然後考慮\([i,j]\)區間的平均值怎樣才能不小於\(k\)呢?

\[\frac{s[j]-s[i-1]}{j-i+1}\geq k\]

\[s[j]-k\times j\geq s[i-1]-(i-1)\times k\]

誒,那對於每個\(j\)查詢有多少個\(i\)滿足上述式子不就好了嗎?

在這裡我選擇了平衡樹完成查詢,平衡樹選用的是\(fhq\ Treap\)

還可以考慮的是,與處理出所有的\(s[i-1]-(i-1)\times k\),然後離散化弄到線段樹上做,也是可行的,不過程式碼我就沒有寫了。

AC程式碼

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MAXN=2e5+5;
LL n,k,cnt,rt,ans,a[MAXN],s[MAXN];
struct Node
{
    LL val,rnd,sz;
    LL ls,rs;
    #define val(a) node[a].val
    #define rnd(a) node[a].rnd
    #define sz(a) node[a].sz
    #define ls(a) node[a].ls
    #define rs(a) node[a].rs
}node[MAXN];
LL read()
{
    LL re=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
    return re;
}
LL new_node(LL v)
{
    val(++cnt)=v;
    rnd(cnt)=rand();
    sz(cnt)=1;
    ls(cnt)=rs(cnt)=0;
    return cnt;
}
void update(LL p){sz(p)=sz(ls(p))+sz(rs(p))+1;}
LL merge(LL x,LL y)
{
    if(!x||!y) return x+y;
    if(rnd(x)<rnd(y))
    {
        rs(x)=merge(rs(x),y);
        update(x);
        return x;
    }
    else
    {
        ls(y)=merge(x,ls(y));
        update(y);
        return y;
    }
}
void split(LL now,LL v,LL &x,LL &y)
{
    if(!now) x=y=0;
    else
    {
        if(val(now)<=v)
        {
            x=now;
            split(rs(now),v,rs(now),y);
        }
        else
        {
            y=now;
            split(ls(now),v,x,ls(now));
        }
        update(now);
    }
}
int main()
{
    srand(time(0));
    n=read(),k=read();
    for(LL i=1;i<=n;i++) a[i]=read(),s[i]=s[i-1]+a[i];
    for(LL i=1;i<=n;i++)
    {
        LL x,y;
        split(rt,s[i]-k*i,x,y);
        ans+=sz(x);
        rt=merge(x,y);
        split(rt,s[i-1]-k*(i-1),x,y);
        rt=merge(merge(x,new_node(s[i-1]-k*(i-1))),y);
        if(s[i]-k*i>=s[i-1]-k*(i-1)) ans++;
    }
    printf("%lld",ans);
    return 0;
}