[loj 6253] Yazid的新生舞会
(很久之前刷的题现在看起来十分陌生a)
题意:
给你一个长度为n的序列A,定义一个区间$[l,r]$是“新生舞会的”当且仅当该区间的众数次数严格大于$\frac{r-l+1}{2}$,求有多少子区间是“新生舞会的”。
$n\leq 500000,0\leq A_{i} \leq n-1$
题解:
关于区间众数的问题一般有一个套路:枚举众数后转换成区间求和问题。
考虑枚举众数k,若将序列中等于k的元素视作+1,其他视作-1,那么“新生舞会的“区间必然满足区间之和大于0。
问题变成了如何快速求出有多少个区间之和大于0的子区间。
考虑枚举右端点r并记录前缀和sum,那么要求的就是有多少左端点l满足$sum[l]<sum[r]$
直接用一个线段树维护即可。(树状数组也行,但跟后面的做法无关)
但是这样复杂度是$O(n^2)$的,无法通过本题。
注意到枚举众数后,序列中1的个数的总数是n。
容易发现连续的-1总数也为n,那么我们考虑将一段连续的-1一起考虑。
假设一段-1的起点是l,终点是r,sum值i出现了$cnt(i)$次,那么这段-1作为右端点时对答案的贡献之和就是
$\sum_{i=l}^{r}{\sum_{j=-inf}^{i-1}{cnt(j)}}$
$=\sum_{j=-inf}^{r-1}{\sum_{i=max(j+1,l)}^{r}{cnt(j)}}$
$=(r-l+1)\times{\sum_{i=-inf}^{l-1}{cnt(i)}}+\sum_{i=l}^{r}{(r-i)\times cnt(j)}$
那么直接维护两个线段树表示$cnt(i)$和$cnt(i)\times i$即可。
对于每段-1,统计答案后在其对应值域区间加,然后计算单个1的贡献即可。
复杂度$O(nlogn)$,比较难写,特别是清空线段树时需要注意clear标记的写法。
代码:
#include<bits/stdc++.h>
#define maxn 2000005
#define maxm 500005
#define inf 0x7fffffff
#define ll long long using namespace std;
ll N,M,A[maxn],B[maxn],lz1[maxn<<],lz2[maxn<<];
ll s1[maxn<<],s2[maxn<<],vis[maxn];
vector<ll> pos[maxn]; inline ll read(){
ll x=,f=; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
} inline void pushup1(ll k){s1[k]=s1[k<<]+s1[k<<|];}
inline void pushdown1(ll len,ll k){
if(k>=(maxn<<)) return;
if(!lz1[k]) return;
if(lz1[k]==-){
s1[k<<]=s1[k<<|]=;
lz1[k<<]=lz1[k<<|]=-;
lz1[k]=; return;
}
if(lz1[k<<]==-) pushdown1(len+>>,k<<),lz1[k<<]=,s1[k<<]=;
if(lz1[k<<|]==-) pushdown1(len>>,k<<|),lz1[k<<|]=,s1[k<<|]=;
lz1[k<<]+=lz1[k],lz1[k<<|]+=lz1[k];
s1[k<<]+=lz1[k]*((len+)>>),s1[k<<|]+=lz1[k]*((len)>>);
lz1[k]=; return;
}
inline ll qry1(ll x,ll y,ll l,ll r,ll k){
pushdown1(r-l+,k);
if(x<=l && r<=y) return s1[k];
ll mid=(l+r)>>,ans=;
if(x<=mid) ans+=qry1(x,y,l,mid,k<<);
if(y>mid) ans+=qry1(x,y,mid+,r,k<<|);
return ans;
}
inline void upd1(ll x,ll y,ll l,ll r,ll k){
pushdown1(r-l+,k);
if(x<=l && r<=y){s1[k]+=(r-l+),lz1[k]++;return;}
ll mid=(l+r)>>;
if(x<=mid) upd1(x,y,l,mid,k<<);
if(y>mid) upd1(x,y,mid+,r,k<<|);
pushup1(k); return;
} inline void pushup2(ll k){s2[k]=s2[k<<]+s2[k<<|];}
inline ll dc(ll x,ll y){return (x+y)*(y-x+)/;}
inline void pushdown2(ll l,ll r,ll k){
if(k>=(maxn<<)) return;
if(!lz2[k]) return;
if(lz2[k]==-){
s2[k<<]=s2[k<<|]=;
lz2[k<<]=lz2[k<<|]=-;
lz2[k]=; return;
}
ll mid=(l+r)>>;
if(lz2[k<<]==-) pushdown2(l,mid,k<<),lz2[k<<]=,s2[k<<]=;
if(lz2[k<<|]==-) pushdown2(mid+,r,k<<|),lz2[k<<|]=,s2[k<<|]=;
lz2[k<<]+=lz2[k],lz2[k<<|]+=lz2[k];
s2[k<<]+=lz2[k]*dc(l,mid),s2[k<<|]+=lz2[k]*dc(mid+,r);
lz2[k]=; return;
}
inline ll qry2(ll x,ll y,ll l,ll r,ll k){
pushdown2(l,r,k);
if(x<=l && r<=y) return s2[k];
ll mid=(l+r)>>,ans=;
if(x<=mid) ans+=qry2(x,y,l,mid,k<<);
if(y>mid) ans+=qry2(x,y,mid+,r,k<<|);
return ans;
}
inline void upd2(ll x,ll y,ll l,ll r,ll k){
pushdown2(l,r,k);
if(x<=l && r<=y){s2[k]+=dc(l,r),lz2[k]++;return;}
ll mid=(l+r)>>;
if(x<=mid) upd2(x,y,l,mid,k<<);
if(y>mid) upd2(x,y,mid+,r,k<<|);
pushup2(k); return;
} inline ll calc(ll x){
ll ans=,sum=N; pos[x].push_back(N+);
for(ll i=;i<pos[x].size()-;i++){
ll lp=pos[x][i]+,rp=pos[x][i+]-;
if(pos[x][i]!=) sum++;
ans+=qry1(,sum-,,M,);
ll tp=qry1(,sum-,,M,);
upd1(sum,sum,,M,),upd2(sum,sum,,M,);
if(lp<=rp){
ll l=sum-(rp-lp+),r=sum-; sum-=(rp-lp+);
ll num=(l==)?:(qry1(,l-,,M,)*(r-l+))+((r)*qry1(l,r-,,M,)-qry2(l,r-,,M,));
ans+=num,upd1(l,r,,M,),upd2(l,r,,M,);
}
}
s1[]=s2[]=,lz1[]=lz2[]=-;
return ans;
} int main(){
N=read(),M=(N<<|); ll not_used=read();
for(ll i=;i<=N;i++) A[i]=read()+;
for(ll i=;i<=N;i++)
if(!vis[A[i]]) vis[A[i]]=,pos[A[i]].push_back();
for(ll i=;i<=N;i++) pos[A[i]].push_back(i);
memset(vis,,sizeof(vis)); ll ans=;
for(ll i=;i<=N;i++)
if(!vis[A[i]]) vis[A[i]]=,ans+=calc(A[i]);
printf("%lld\n",ans);
return ;
}
Yazid
[loj 6253] Yazid的新生舞会的更多相关文章
- [LOJ 6253] Yazid 的新生舞会
link $solution:$ 不知道为什么别人的代码能写的非常短,难道就是写差分的好处? 这种题肯定是算每个众数的贡献,考虑通过暴力众数求出个数. 现在考虑众数 $x$ ,则在序列 $a$ 中将等 ...
- 【BZOJ5110】[CodePlus2017]Yazid 的新生舞会 线段树
[BZOJ5110][CodePlus2017]Yazid 的新生舞会 Description Yazid有一个长度为n的序列A,下标从1至n.显然地,这个序列共有n(n+1)/2个子区间.对于任意一 ...
- bzoj5110: [CodePlus2017]Yazid 的新生舞会
Description Yazid有一个长度为n的序列A,下标从1至n.显然地,这个序列共有n(n+1)/2个子区间.对于任意一个子区间[l,r] ,如果该子区间内的众数在该子区间的出现次数严格大于( ...
- 【bzoj5110】Yazid的新生舞会
这里是 $THUWC$ 选拔时间 模拟赛的时候犯 $SB$ 了,写了所有的部分分,然后直接跑过了 $4$ 个大样例(一个大样例是一个特殊情况)…… 我还以为我非常叼,部分分都写对了,于是就不管了…… ...
- BZOJ.5110.[CodePlus2017]Yazid 的新生舞会(线段树/树状数组/分治)
LOJ BZOJ 洛谷 又来发良心题解啦 \(Description\) 给定一个序列\(A_i\).求有多少个子区间,满足该区间众数出现次数大于区间长度的一半. \(n\leq5\times10^5 ...
- 【BZOJ5110】[CodePlus2017]Yazid 的新生舞会
题解: 没笔的时候我想了一下 发现如果不是出现一半次数而是k次,并不太会做 然后我用前缀和写了一下发现就是维护一个不等式: 于是就可以随便维护了
- 「CodePlus 2017 11 月赛」Yazid 的新生舞会(树状数组/线段树)
学习了新姿势..(一直看不懂大爷的代码卡了好久T T 首先数字范围那么小可以考虑枚举众数来计算答案,设当前枚举到$x$,$s_i$为前$i$个数中$x$的出现次数,则满足$2*s_r-r > 2 ...
- 【bzoj5110】[CodePlus2017]Yazid 的新生舞会 Treap
题目描述 求一个序列所有的子区间,满足区间众数的出现次数大于区间长度的一半. 输入 第一行2个用空格隔开的非负整数n,type,表示序列的长度和数据类型.数据类型的作用将在子任务中说明. 第二行n个用 ...
- [BZOJ5110]Yazid的新生舞会
题目大意: 给你一个长度为$n(n\leq 5\times 10^5)$的序列$A_{1\sim n}$.求满足区间众数在区间内出现次数严格大于$\lfloor\frac{r-l+1}{2}\rflo ...
随机推荐
- 获取url后的参数、获取a标签的参数
function getQueryString(name) { var reg = new RegExp("(^|&)" + name + "=([^&] ...
- 【Dubbo】带着问题看源码:什么是SPI机制?Dubbo是如何实现的?
什么是SPI? 在Java中,SPI全称为 Service Provider Interface,是一种典型的面向接口编程机制.定义通用接口,然后具体实现可以动态替换,和 IoC 有异曲同工之妙. ...
- mysql 8.0.18 hash join测试(内外网首文)
CREATE TABLE COLUMNS_hj as select * from information_schema.`COLUMNS`; INSERT INTO COLUMNS_hj SELECT ...
- 洛谷 P1969 积木大赛(NOIP2013)
题目描述春春幼儿园举办了一年一度的“积木大赛”.今年比赛的内容是搭建一座宽度为n的大厦,大厦可以看成由n块宽度为1的积木组成,第i块积木的最终高度需要是hi. 在搭建开始之前,没有任何积木(可以看成n ...
- 【C++】C++中的类模板
基础的类模板 模板类的继承 内部声明定义普通模板函数和友元模板函数 内部声明友元模板函数+外部定义友元模板函数 声明和定义分别在不同的文件(模板函数.模板友元) C++中有一个重要特性,那就是模板类型 ...
- ThinkPHP中文字段问题
转自: https://www.baidu.com/link?url=Ohc9epgQgkNYLwnHqP-jZ9RfIQWW50-iz8-ZMIPLdtCIJHnUpYwQnDLmXzi7Fa110 ...
- sklearn的基本使用
https://cloud.tencent.com/developer/news/58202 简介 今天为大家介绍的是scikit-learn.sklearn是一个Python第三方提供的非常强力的机 ...
- Python3基础 is与== 区别
Python : 3.7.3 OS : Ubuntu 18.04.2 LTS IDE : pycharm-community-2019.1.3 ...
- 安装opencv时ippicv下载超时
1.手动去下载: github地址为: https://github.com/opencv/opencv_3rdparty/tree/ippicv/master_20151201/ippicv 2.查 ...
- Laravel 登录后跳转回登录前浏览的页面
一.经过 Auth 中间件检查后跳转至登录页面 也就是没有通过 auth 中间件的认证检查,被 auth 中间件拦截后跳转至登录页面.这种情况下,Laravel 默认会在用户登录成功后自动跳转回登录前 ...