zoukankan      html  css  js  c++  java
  • 【洛谷P3567】KURCouriers

    题目

    题目链接: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;
    }
    
  • 相关阅读:
    关于负数补码的求解
    二维数组的行列指针
    复杂类型的解读
    单斜杠''的思考
    HTML 文本格式化实例--常用的标签
    P1 基础知识以及客户截面 【B站 SolidWorks2014教学视频 共计20讲】
    SolidWorks 2-5 草图的绘制
    SolidWorks 2-4 草图简介
    SolidWorks 模型创建的一般过程 2019年2月25日
    P3 3.HTML&CSS基础_HTML简介 (24'22")---------- 高质量HTML与CSS基础(共103讲)
  • 原文地址:https://www.cnblogs.com/stoorz/p/12670801.html
Copyright © 2011-2022 走看看