【洛谷P3567】KUR-Couriers

题目

题目链接:https://www.luogu.com.cn/problem/P3567
给一个数列,每次询问一个区间内有没有一个数出现次数超过一半。

思路

一开始随机化瞎搞了 \(80pts\) 233。
看见大部分题解写的都是主席树,就是维护前 \(i\) 个数的值域,每次往超过一半的部分走。时间复杂度 \(O(Q\log n)\)
但是这题可以用线段树的,虽然好像更麻烦。
首先有一个性质:对于一个数列,如果其中存在一个数的出现次数严格大于一半,那么每次在这个数列中删除两个不同的数,不能再删除的时候,剩下的数就是出现次数严格大于一半的数。
线段树维护区间 \(val,cnt\),分别表示现在删除后剩余的数,以及这个数字剩余的个数。显然这是满足集合率的:对于区间 \([l,r]\)(记为 \(a\) ),设它的左右子树分别为 \(x,y\)

  • 如果 \(val[x]=val[y]\),则 \(val[a]=val[x],cnt[a]=cnt[x]+cnt[y]\)
  • 否则 \(val[a]=cnt\) 更大的那一边,\(cnt[a]=\max(cnt[x],cnt[y])-\min(cnt[x],cnt[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;
}

猜你喜欢

转载自www.cnblogs.com/stoorz/p/12670801.html