1. 程式人生 > >bzoj5110 [CodePlus2017]Yazid 的新生舞會 線段樹

bzoj5110 [CodePlus2017]Yazid 的新生舞會 線段樹

Description


Yazid有一個長度為n的序列A,下標從1至n。顯然地,這個序列共有n(n+1)/2個子區間。對於任意一個子區間[l,r]
,如果該子區間內的眾數在該子區間的出現次數嚴格大於(r?l+1)/2(即該子區間長度的一半),那麼Yazid就說這
個子區間是"新生舞會的"。所謂眾數,即為該子區間內出現次數最多的數。特別地,如果出現次數最多的數有多個
,我們規定值最小的數為眾數。現在,Yazid想知道,共有多少個子區間是"新生舞會的"

N<=500000,0<=Type<=3
對於所有資料,保證 0 ≤ Ai ≤ n ? 1。
對於 type = 0 的資料,沒有任何特殊約定。
對於 type = 1 的資料,保證 Ai ∈ {0, 1}。
對於 type = 2 的資料,保證序列 A 的眾數在整個序列中的出現次數不超過 15。
對於 type = 3 的資料,保證 Ai ≤ 7。

Solution


想起了今年做cp,體驗極差

一個暴力的做法是列舉眾數,把眾數看作1,其餘看作-1,合法區間就是和>0的區間數量,我們列舉右端點對字首和開權值線段樹就可以了
考慮怎麼優化這個暴力。對於一整段的-1我們合併貢獻。記-1所在區間為[l,r],那麼右端點在l時查詢值域在[-n,sum-2],在l+1時是[-n,sum-3],在i時是[-n,sum-1-(i-l+1)],並且[-n,sum-1-(r-l+1)]這一段會被算(r-l+1)次。注意到這等價於求一個梯形區域,我們線段樹維護rec[x]和rec[x]*x就可以求了

Code


#include
<stdio.h>
#include <string.h> #include <algorithm> #include <vector> #define rep(i,st,ed) for (int i=st;i<=ed;++i) #define fi first #define se second typedef long long LL; typedef std:: pair <LL,LL> pair; const int N=1000010; std:: vector <int> vec[N]; LL sum1[N<<
2],sum2[N<<2]; LL tag[N<<2]; bool clr[N<<2]; pair operator +(pair a,pair b) { return pair(a.fi+b.fi,a.se+b.se); } int read() { int x=0,v=1; char ch=getchar(); for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar()); for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar()); return x*v; } void push_down(int now,int tl,int tr,int mid) { if (clr[now]) { clr[now]=0; clr[now<<1]=clr[now<<1|1]=1; sum1[now<<1]=sum2[now<<1]=tag[now<<1]=0; sum1[now<<1|1]=sum2[now<<1|1]=tag[now<<1|1]=0; } if (tag[now]) { LL w=tag[now]; tag[now]=0; tag[now<<1]+=w; tag[now<<1|1]+=w; sum1[now<<1]+=w*(mid-tl+1); sum1[now<<1|1]+=w*(tr-mid); sum2[now<<1]+=w*(tl+mid)*(mid-tl+1)/2; sum2[now<<1|1]+=w*(mid+1+tr)*(tr-mid)/2; } } void modify(int now,int tl,int tr,int l,int r,LL v) { if (r<l) return ; if (tl>=l&&tr<=r) { sum1[now]+=v*(r-l+1); sum2[now]+=v*(r+l)*(r-l+1)/2; tag[now]+=v; return ; } int mid=(tl+tr)>>1; push_down(now,tl,tr,mid); modify(now<<1,tl,mid,l,std:: min(r,mid),v); modify(now<<1|1,mid+1,tr,std:: max(mid+1,l),r,v); sum1[now]=sum1[now<<1]+sum1[now<<1|1]; sum2[now]=sum2[now<<1]+sum2[now<<1|1]; } pair query(int now,int tl,int tr,int l,int r) { if (r<l) return pair(0,0); if (tl>=l&&tr<=r) return pair(sum1[now],sum2[now]); int mid=(tl+tr)>>1; push_down(now,tl,tr,mid); pair qx=query(now<<1,tl,mid,l,std:: min(r,mid)); pair qy=query(now<<1|1,mid+1,tr,std:: max(mid+1,l),r); return qx+qy; } int main(void) { freopen("data.in","r",stdin); freopen("myp.out","w",stdout); int n=read(); read(); rep(i,1,n) { int x=read(); vec[x].push_back(i); } LL ans=0; for (int now=0;now<n;++now) { if (!vec[now].size()) continue; clr[1]=1; sum1[1]=sum2[1]=tag[1]=0; modify(1,-n,n,0,0,1); vec[now].push_back(n+1); for (int sum=0,L=1,i=0;i<vec[now].size();++i) { int R=vec[now][i]-1; if (L<=R) { ans+=query(1,-n,n,-n,sum-1).fi*(1LL*R-L+1); ans-=query(1,-n,n,sum-(R-L+1),sum-1).se; ans+=query(1,-n,n,sum-(R-L+1),sum-1).fi*(1LL*sum-1-(R-L+1)); modify(1,-n,n,sum-(R-L+1),sum-1,1); sum-=(R-L+1); } L=vec[now][i]+1; if (i+1!=vec[now].size()) { sum++,ans+=query(1,-n,n,-n,sum-1).fi; modify(1,-n,n,sum,sum,1); } } } printf("%lld", ans); return 0; }