题目链接:https://www.luogu.com.cn/problem/P3567
给一个数列,每次询问一个区间内有没有一个数出现次数超过一半。
一开始随机化瞎搞了 \(80pts\) 233。
看见大部分题解写的都是主席树,就是维护前 \(i\) 个数的值域,每次往超过一半的部分走。时间复杂度 \(O(Q\log n)\)。
但是这题可以用线段树的,虽然好像更麻烦。
首先有一个性质:对于一个数列,如果其中存在一个数的出现次数严格大于一半,那么每次在这个数列中删除两个不同的数,不能再删除的时候,剩下的数就是出现次数严格大于一半的数。
线段树维护区间 \(val,cnt\),分别表示现在删除后剩余的数,以及这个数字剩余的个数。显然这是满足集合率的:对于区间 \([l,r]\)(记为 \(a\) ),设它的左右子树分别为 \(x,y\)
但是容易发现,如果存在一个数超过一半,必然就是我们求出来的数;但是求出来的数不一定在原序列中出现次数超过一半。
设我们求出来的数为 \(x\),那么这个区间答案就只有为 \(x\) 或不存在两种选项。我们只需判断 \(x\) 在该区间是否出现超过一半即可。
由于这道题没有修改,可以直接开 \(n\) 个 \(\operatorname{vector}\),第 \(i\) 个 \(\operatorname{vector}\) 记录数字 \(i\) 出现的位置。然后再 \(\operatorname{vector}\) 上二分即可。
拓展:如果有修改,那么手写任意一棵平衡树即可。时间复杂度均为 \(O(\log n)\)。
总复杂度 \(O(Q\log n)\)。
输出超级慢,加上输出优化直接快了 \(1.5s\)。
#pragma GCC optimize("Ofast","inline")
#include <vector>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=500010;
int n,Q,a[N],rt[N];
vector<int> pos[N];
int read()
{
int d=0; char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
return d;
}
void write(int x)
{
if (x>10) write(x/10);
putchar(x%10+48);
}
struct Seg
{
int l[N*4],r[N*4],cnt[N*4],val[N*4];
void pushup(int x)
{
if (val[x*2]==val[x*2+1])
val[x]=val[x*2],cnt[x]=cnt[x*2]+cnt[x*2+1];
else if (cnt[x*2]>cnt[x*2+1])
val[x]=val[x*2],cnt[x]=cnt[x*2]-cnt[x*2+1];
else
val[x]=val[x*2+1],cnt[x]=cnt[x*2+1]-cnt[x*2];
}
void build(int x,int ql,int qr)
{
l[x]=ql; r[x]=qr;
if (ql==qr)
{
val[x]=a[ql]; cnt[x]=1;
return;
}
int mid=(l[x]+r[x])>>1;
build(x*2,ql,mid); build(x*2+1,mid+1,qr);
pushup(x);
}
void query(int x,int ql,int qr,int &p,int &q)
{
if (l[x]==ql && r[x]==qr)
{
p=val[x]; q=cnt[x];
return;
}
int mid=(l[x]+r[x])>>1;
if (qr<=mid) query(x*2,ql,qr,p,q);
else if (ql>mid) query(x*2+1,ql,qr,p,q);
else
{
int p1,p2,q1,q2;
query(x*2,ql,mid,p1,q1);
query(x*2+1,mid+1,qr,p2,q2);
if (p1==p2) p=p1,q=q1+q2;
else if (q1<q2) p=p2,q=q2-q1;
else p=p1,q=q1-q2;
}
}
}seg;
int main()
{
n=read(); Q=read();
for (int i=1;i<=n;i++)
{
a[i]=read();
pos[a[i]].push_back(i);
}
seg.build(1,1,n);
while (Q--)
{
int l=read(),r=read(),x,WYCAKIOI;
seg.query(1,l,r,x,WYCAKIOI);
int p=lower_bound(pos[x].begin(),pos[x].end(),l)-pos[x].begin();
int q=upper_bound(pos[x].begin(),pos[x].end(),r)-pos[x].begin();
if ((q-p)*2>r-l+1) write(x);
else putchar(48);
putchar(10);
}
return 0;
}
原文:https://www.cnblogs.com/stoorz/p/12670801.html