Luogu 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; }