• 【洛谷P4168】蒲公英【分块】


    题目大意:

    题目链接:https://www.luogu.org/problemnew/show/P4168
    给出一个长度为nn的序列,mm次询问,求区间[l,r][l,r]中的众数。强制在线。


    思路:

    以下内容参考lydlyd《算法竞赛进阶指南》。

    先离散化不解释。
    分块算法一般都是以“暴力+区间预处理+暴力”的方法做的。所以,我们把nn分成TT块,每块长度Tn⌊frac{T}{n}⌋
    然后预处理出对于任意两个区间内的数字个数。也就是说,对于任意的1lrT1leq lleq rleq T,我们用cnt[L][R][i]cnt[L][R][i]表示块LRLsim R内数字ii的个数。
    如何预处理出这一部分?

    • 方法1
      先暴力处理出所有的cnt[i][i][j]cnt[i][i][j],然后用区间dpdp的思想,求出所有的cnt[L][R][j]cnt[L][R][j],时间复杂度O(T2n)O(T^2n)
    • 方法2
      预处理出sum[R][i]sum[R][i]表示区间1R1sim R之间ii的个数。然后用前缀和思想,cnt[L][R][i]=sum[R][i]sum[L][i]cnt[L][R][i]=sum[R][i]-sum[L][i]。时间复杂度O(T2)O(T^2)

    同时在预处理cntcnt的时候顺便处理出Max[L][R]Max[L][R],表示区间众数,然后记录众数的个数。
    接下来就是如何处理答案了。
    对于任意一组询问,设询问区间[l,r][l,r],那么先记录q,pq,p分别表示l,rl,r所在的块。如果pq1p-qleq 1,那么这两个块中间就没有多余的整块,直接朴素求众数。
    否则在cnt[q+1][p1]cnt[q+1][p-1]的整块区间中,加上[l,R[q]],[L[p],r][l,R[q]],[L[p],r]之间的数字,然后求出答案。最后再减去加上的数就可以了。


    算法时间复杂度为O(nT2+mTn)O(nT^2+frac{mT}{n}),空间复杂度为O(nT2)O(nT^2)。为了平均时间,不妨设nT2=mTnnT^2=frac{mT}{n},解得Tn3T≈sqrt[3]{n}。此时时间复杂度约为O(n53)O(n^{frac{5}{3}})


    代码:

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    using namespace std;
    
    const int N=50010;
    int a[N],b[N],cnt[40][40][N],L[40],R[40],pos[N],sum[N],Max[40][40][2],be[N];
    int n,m,T,x,y,len,tot,last;
    
    int ask(int l,int r)
    {
    	int ans=0,maxn=0;
    	int q=pos[l],p=pos[r];
    	if (p-q<=1)
    	{
    		memset(sum,0,sizeof(sum));
    		for (int i=l;i<=r;i++)
    		{
    			sum[a[i]]++;
    			if (sum[a[i]]>maxn||(sum[a[i]]==maxn&&a[i]<ans))
    				maxn=sum[a[i]],ans=a[i];
    		}
    		return ans;
    	}
    	maxn=Max[q+1][p-1][0];
    	ans=Max[q+1][p-1][1];
    	for (int i=l;i<=R[q];i++)
    	{
    		cnt[q+1][p-1][a[i]]++;
    		if (cnt[q+1][p-1][a[i]]>maxn||(cnt[q+1][p-1][a[i]]==maxn&&a[i]<ans))
    			maxn=cnt[q+1][p-1][a[i]],ans=a[i];
    	}
    	for (int i=L[p];i<=r;i++)
    	{
    		cnt[q+1][p-1][a[i]]++;
    		if (cnt[q+1][p-1][a[i]]>maxn||(cnt[q+1][p-1][a[i]]==maxn&&a[i]<ans))
    			maxn=cnt[q+1][p-1][a[i]],ans=a[i];
    	}
    	for (int i=l;i<=R[q];i++) cnt[q+1][p-1][a[i]]--;
    	for (int i=L[p];i<=r;i++) cnt[q+1][p-1][a[i]]--;
    	return ans;
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		b[i]=a[i];
    	}
    	sort(b+1,b+1+n);
    	tot=unique(b+1,b+1+n)-b-1;
    	for (int i=1;i<=n;i++)
    	{
    		x=lower_bound(b+1,b+1+tot,a[i])-b;
    		be[x]=a[i];
    		a[i]=x;
    	}
    	T=(int)pow((double)n,1.0/3.0);
    	len=n/T;
    	if (T*len<n) T++;
    	for (int i=1;i<=T;i++)
    	{
    		L[i]=R[i-1]+1;
    		R[i]=min(i*len,n);
    		for (int j=L[i];j<=R[i];j++)
    		{
    			cnt[i][i][a[j]]++;
    			if (cnt[i][i][a[j]]>Max[i][i][0]||(cnt[i][i][a[j]]==Max[i][i][0]&&a[j]<Max[i][i][1]))
    			{
    				Max[i][i][0]=cnt[i][i][a[j]];
    				Max[i][i][1]=a[j];
    			}
    			pos[j]=i;
    		}
    	}
    	for (int k=1;k<T;k++)
    		for (int i=1;i<=T-k;i++)
    		{
    			int j=i+k;
    			int mid=(i+j)/2;
    			for (int l=1;l<=tot;l++)
    			{
    				cnt[i][j][l]=cnt[i][mid][l]+cnt[mid+1][j][l];
    				if (cnt[i][j][l]>Max[i][j][0])
    				{
    					Max[i][j][0]=cnt[i][j][l];
    					Max[i][j][1]=l;
    				}
    			}
    		}
    	while (m--)
    	{
    		scanf("%d%d",&x,&y);
    		x=(x+last-1)%n+1;
    		y=(y+last-1)%n+1;
    		if (x>y) swap(x,y);
    		last=be[ask(x,y)];
    		printf("%d
    ",last);
    	}
    	return 0;
    }
    
  • 相关阅读:
    洛谷P2568 GCD
    线段树(模板)
    题解 CF1296D 【Fight with Monsters】
    图片针对父元素居中 TileImg
    npm
    echarts线图,柱状图,饼图option
    mac下修改环境变量
    input获取焦点,但不调起键盘
    mac shh 关联git仓库
    根据滚动条触发动画
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998254.html
Copyright © 2020-2023  润新知