bzoj5110 [CodePlus2017]Yazid 的新生舞會 線段樹
阿新 • • 發佈:2018-12-23
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;
}