zoukankan      html  css  js  c++  java
  • P4168 [Violet]蒲公英 题解

    Link

    P4168 [Violet]蒲公英

    Solve

    本题是非常经典的在线求区间众数问题。因为众数不具有区间可加性(已知([x,y])([y+1,z])的众数,不能直接得到([x,z])的众数),所以用树状数组或者线段树维护就特别难,我们考虑分块来做。

    我们把序列分成(T)块,每块长度(L=N/T)

    对于每个询问([l,r]),设(l)处于第(p)块,(r)处于第(q)块,我们可以把([l,r])分成三个部分。

    1.开头不足一段([l,L))

    2.第(p+1)~(q-1)块的区间([L,R])

    3.结尾不足一段((L,R])

    显然,序列在区间([l,r])上的众数只可能来自于以下两种情况

    1.区间([L,R])的众数

    2.出现在([l,L))和((R,r])之间的数

    我们先预处理出义"段边界"为端点的区间([L,R])的众数,然后开(N)(vextor)保存数值(a_i)所在的位置(离散后)。

    对于每个询问扫描([l,L))((R,r])中的每个数(x),在对应的(vector)中二分查找即可得到(x)([l,r])中出现的次数,从而更新答案,

    这个算法的时间为(O(NT+NM/T ast log N)),空间为(O(N^2))。应取(T=sqrt{NlogN}),使得整个算法时间复杂度在(O(Nsqrt{NlogN}))级别。

    代码细节比较多,调了一个早上

    #include<cstdio>
    #include<vector>
    #include<algorithm>
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=40005,maxt=205;
    int N,t,M,p[maxt][maxt],m,L[maxt],R[maxt],pos[maxn],cnt[maxn],last,up[maxn];
    vector <int> Q[maxn];
    struct AS{
    	int x,id,color;
    }a[maxn];
    inline int read(){
    	int ret=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
    	while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
    	return ret*f;
    }
    bool cmp1(AS A,AS B){return A.x<B.x;}
    bool cmp2(AS A,AS B){return A.id<B.id;}
    int get(int l,int r,int x){
    	return upper_bound(Q[x].begin(),Q[x].end(),r)-lower_bound(Q[x].begin(),Q[x].end(),l);
    }
    int query(int l,int r){
    	int max_x=0,num=0,res;
    	if(pos[l]+1<pos[r]){max_x=get(l,r,p[pos[l]+1][pos[r]-1]),num=p[pos[l]+1][pos[r]-1];}
    	for(int i=l;i<=R[pos[l]];i++){
    		res=get(l,r,a[i].color);
    		if(res>max_x||(res==max_x&&a[i].color<num)){max_x=res;num=a[i].color;}
    	}
    	if(pos[l]!=pos[r])
    		for(int i=L[pos[r]];i<=r;i++){
    			res=get(l,r,a[i].color);
    			if(res>max_x||(res==max_x&&a[i].color<num)){max_x=res;num=a[i].color;}
    		}
    	return num;
    }
    int main(){
    	freopen("P4168.in","r",stdin);
    	freopen("P4168.out","w",stdout);
    	N=read();M=read();
    	for(int i=1;i<=N;i++)a[i].x=read(),a[i].id=i;
    	sort(a+1,a+1+N,cmp1);
    	for(int i=1;i<=N;i++){
    		if(a[i].x!=a[i-1].x)++m,up[m]=a[i].x;
    		a[i].color=m;
    	}
    	sort(a+1,a+1+N,cmp2);
    	for(int i=1;i<=N;i++)Q[a[i].color].push_back(i);
    	for(int i=1;i<=m;i++)Q[i].push_back(N+1);
    	t=sqrt(N);
    	for(int i=1;i<=t;i++){
    		L[i]=(i-1)*sqrt(N)+1;
    		R[i]=i*sqrt(N);
    	}
    	if(R[t]<N){t++;L[t]=R[t-1]+1;R[t]=N;}
    	for(int i=1;i<=t;i++){
    		for(int j=L[i];j<=R[i];j++){pos[j]=i;}
    	}
    	for(int i=1;i<=t;i++){
    		memset(cnt,0,sizeof cnt);int max_x=0,num=0;
    		for(int j=L[i];j<=N;j++){
    			cnt[a[j].color]++;
    			if(cnt[a[j].color]>max_x||(cnt[a[j].color]==max_x&&a[j].color<num)){
    				max_x=cnt[a[j].color];num=a[j].color;
    			}
    			p[i][pos[j]]=num;
    		}
    	}
    	while(M--){
    		int l=read(),r=read();
    		l=(l+last-1)%N+1,r=(r+last-1)%N+1;
    		if(l>r)swap(l,r);
    		last=up[query(l,r)];
    		printf("%d
    ",last);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    vue-cli 3.0 路由懒加载
    vue 路由拦截、axios请求拦截
    vue-cli 3.0 图片路径问题(何时使用 public 文件夹)
    vue 监听页面宽度变化 和 键盘事件
    WGS84、GCJ-02(火星坐标)、百度坐标,Web墨卡托坐标
    Java学习之道:Java项目打包发布
    ora-14550问题解决
    费氏搜寻法之算法分析与实现
    [置顶] woff格式字体怎么打开和编辑?
    C++小知识之Vector排序
  • 原文地址:https://www.cnblogs.com/martian148/p/13884192.html
Copyright © 2011-2022 走看看