P4062 [Code+#1] Yazid 的新生舞会
题外话#
我记得第一次看见这道题是几个月前刚开始集训的时候,当时一点思路都没有,但是今天自己做出来了,很喜欢这种感觉!
#
题意#
求给定序列中有多少个子区间满足众数出现次数严格大于区间长度的一半。
题解#
题目要求满足条件的子区间,一个很直接的想法是每次固定左(右)端点,求有多少个右(左)可以与其匹配对答案造成贡献。
那么考虑一个暴力做法:每次固定左端点,然后往后面一直扫,枚举每个右端点,中途记录众数出现次数,然后依次判断即可。时间复杂度为
这肯定是过不了的,那么我们再从条件入手,注意到:
- 严格大于区间长度的一半
于是就说明每个区间对应的这个众数只会有一个!考虑利用一下这个性质。
那么我们可以枚举这个众数。设我们当前枚举的众数为
沿用上面固定一个端点求另一个合法端点数量的思路,对于一个右端点
不等式两边同时
移项得:
记
经典问题,树状数组维护即可。但时间复杂度为
那么我们考虑整体观察一下序列
显然地,如果满足
记
因为公差为
沿用上面树状数组的做法,开一个桶
然后它又可以写成两个前缀和相减的形式,也就成了
呵,果然又变成大力 ds 了是吧
线段树和树状数组都可以维护,在我看来各有优势吧,线段树写此题代码的时候比较容易理解,好写一点,但这题树状数组的常数吊打线段树。
这里给出线段树的实现方式:
哦对,因为
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define il inline
#define re register
const int N=5e5+113;
int n,a[N],b[N],ans,dt,vmax;
pii lst[N];
bitset<N>solved;
vector<int>v[N];
struct SegT{
int ans,sum,tag,l,r;
}L[N<<3];
#define ls (id<<1)
#define rs (id<<1|1)
il void Pushup(SegT &fa,SegT lson,SegT rson){
fa.sum=lson.sum+rson.sum;
int l=lson.l,mid=lson.r,r=rson.r;
fa.ans=lson.ans+rson.ans+lson.sum*(r-mid);
}
il int Get(int x){
return (x*(x+1))>>1;
}
il void Add(SegT &fa,int x){
fa.tag+=x,fa.sum+=(fa.r-fa.l+1)*x,fa.ans+=Get(fa.r-fa.l+1)*x;
}
il void Pushdown(SegT &fa,SegT &lson,SegT &rson){
if(!fa.tag)return;
Add(lson,fa.tag),Add(rson,fa.tag);
fa.tag=0;
}
il void Add(int id,int l,int r,int x,int y,int z){
if(l>=x&&r<=y){
Add(L[id],z);
return;
}
Pushdown(L[id],L[ls],L[rs]);
int mid=(l+r)>>1;
if(x<=mid)Add(ls,l,mid,x,y,z);
if(y>mid)Add(rs,mid+1,r,x,y,z);
Pushup(L[id],L[ls],L[rs]);
}
il SegT GetAns(int id,int l,int r,int x,int y){
if(l>=x&&r<=y)return L[id];
Pushdown(L[id],L[ls],L[rs]);
int mid=(l+r)>>1;
SegT res;
if(x<=mid&&y>mid){
SegT Left=GetAns(ls,l,mid,x,y);
SegT Right=GetAns(rs,mid+1,r,x,y);
Pushup(res,Left,Right);
}
else if(x<=mid)res=GetAns(ls,l,mid,x,y);
else res=GetAns(rs,mid+1,r,x,y);
Pushup(L[id],L[ls],L[rs]);
return res;
}
il void Build(int id,int l,int r){
L[id]={0,0,0,l,r};
if(l==r)return;
int mid=(l+r)>>1;
Build(ls,l,mid),Build(rs,mid+1,r);
}
il void solve(int x){
solved[x]=1;
int siz=v[x].size()-1,mx=dt,mn=dt;
for(re int i=0;i<=siz;i++)
b[i]=(i<<1)-v[x][i]+dt,mx=max(mx,b[i]),mn=min(mn,b[i]);
for(re int i=0;i<=siz;i++){
int r=b[i],l=(i==siz)?b[i]-(n-v[x][i]):b[i]-(v[x][i+1]-v[x][i]-1);
ans+=GetAns(1,1,vmax,1,r-1).ans-GetAns(1,1,vmax,1,l-2).ans;
Add(1,1,vmax,l,r,1);
}
for(re int i=0;i<=siz;i++){
int r=b[i],l=(i==siz)?b[i]-(n-v[x][i]):b[i]-(v[x][i+1]-v[x][i]-1);
Add(1,1,vmax,l,r,-1);
}
}
il int read(){
re int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*f;
}
signed main(){
n=read(),a[0]=read();
dt=n+10,vmax=n+dt;
for(re int i=1;i<=n;i++){
a[i]=read();
if(v[a[i]].empty())v[a[i]].push_back(0);
v[a[i]].push_back(i);
}
Build(1,1,vmax);
for(re int i=1;i<=n;i++)
if(!solved[a[i]])solve(a[i]);
cout<<ans;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)