zoukankan      html  css  js  c++  java
  • [Violet]蒲公英(分块)

    题意

    静态区间求众数(出现次数相同输出值较小的,(n leq 4e4) , (q leq 5e4)),强制在线

    思路

    由于n比较小,但是直接(n^2)又过不了,众数这个信息又不好用log数据结构维护(不容易合并两区间),所以考虑根号数据结构,离线静态区间众数可以用莫队完成啦,但是由于这道题强制在线,所以我们考虑分块做法

    即使是分块也无法简单的合并两个块的数据,所以我们考虑分块的本质—— 暴力,直接维护块合并后的信息

    1. (f[i][j])表示第i块到第j块这一段的众数,那么接下来就可以只考虑怎么加入两头的数

    2. 考虑暴力求众数的做法:记录当前的众数以及所有数的出现次数,如果新加入的数的出现次数大于众数,那么它取代原众数成为新的众数

    3. 改进暴力做法。同样,我们每次询问开了一个桶存众数的出现次数,但问题是我们不能暴力加每一个数,所以我们还要知道第i个块到第j个块这一段中每一个数的出现次数,于是我们要开一个三维数组[i][j][k],这样显然会爆内存,改进方法显然就是前缀和,用sum[i][k]表示存了前i块数据的桶即可

    4. 查询一个区间,我们要先读取i到j块的桶,但暴力每个数都读取仍旧会(O(n))超时,所以我们考虑哪一些不可能成为众数,就不读取它的桶,思考发现除了f[i][j]之外可能成为众数的只有边角上出现了的数,只读取它们的桶就可以做到(sqrt{n})

    总结思路:预处理出f,sum两个数组,大块直接调取f[i][j],边角先从sum中读取桶再按照暴力方法直接加数

    Code:

    #include<bits/stdc++.h>
    #define N 40005
    #define S 205
    using namespace std;
    int n,m,x=0,a[N];
    int size,cnt,sum[S][N],f[S][S];
    int L[S],R[S],pos[N],tng[N];
    int b[N],len;
    template <class T>
    inline void read(T &x)
    {
    	char c;int sign=1;
    	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    	while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
    }
    inline int get_lit(int x) { return lower_bound(b+1,b+len+1,x)-b; } 
    void init()
    {
    	sort(b+1,b+n+1);
    	len=unique(b+1,b+n+1)-b-1;
    	for(int i=1;i<=n;++i) a[i]=get_lit(a[i]);
    	int hfu=0;//丑陋的分块
    	for(int i=1;i<=n;++i)
    	{
    		if(!hfu) L[++cnt]=i;
    		++hfu;
    		pos[i]=cnt;
    		if(hfu==size||i==n) R[cnt]=i,hfu=0;
    	}
    	for(int i=1;i<=n;++i)
    	{
    		if(pos[i]!=pos[i-1]) memcpy(sum[pos[i]],sum[pos[i-1]],sizeof(sum[pos[i-1]]));
    		sum[pos[i]][a[i]]++;
    	}
    	for(int i=1;i<=cnt;++i)
    	{
    		int mode=0,lsw=i;
    		memset(tng,0,sizeof(tng));
    		for(int j=L[i];j<=n;++j)
    		{
    			++tng[a[j]];
    			if((tng[mode]<tng[a[j]])||(tng[mode]==tng[a[j]]&&a[j]<mode)) mode=a[j];
    			if(j==R[lsw]) f[i][lsw++]=mode;
    		}
    	}
    }
    inline int query(int l,int r)
    {
    	int lp=pos[l],rp=pos[r];
    	if(lp==rp)//直接暴力 
    	{
    		int mode=0;
    		for(int i=l;i<=r;++i)
    		{
    			++tng[a[i]];
    			if((tng[mode]<tng[a[i]])||(tng[mode]==tng[a[i]]&&a[i]<mode)) mode=a[i];
    		}
    		for(int i=l;i<=r;++i) --tng[a[i]]; //还原tng数组
    		return mode;
    	}
    	int mode=f[lp+1][rp-1];
    	tng[mode]+=sum[rp-1][mode]-sum[lp][mode];
    	for(int i=l;i<=R[lp];++i)
    	{
    		if(!tng[a[i]]) tng[a[i]]+=(sum[rp-1][a[i]]-sum[lp][a[i]]);
    		++tng[a[i]];
    		if((tng[mode]<tng[a[i]])||(tng[mode]==tng[a[i]]&&a[i]<mode)) mode=a[i];
    	}
    	for(int i=L[rp];i<=r;++i)
    	{
    		if(!tng[a[i]]) tng[a[i]]+=(sum[rp-1][a[i]]-sum[lp][a[i]]);
    		++tng[a[i]];
    		if((tng[mode]<tng[a[i]])||(tng[mode]==tng[a[i]]&&a[i]<mode)) mode=a[i];
    	}
    	for(int i=l;i<=R[lp];++i) tng[a[i]]=0;
    	for(int i=L[rp];i<=r;++i) tng[a[i]]=0;
    	tng[f[lp+1][rp-1]]=0;
    	return mode;
    }
    int main()
    {
    	read(n);read(m);
    	size=sqrt(n);
    	for(int i=1;i<=n;++i) read(a[i]),b[i]=a[i];
    	init();
    	memset(tng,0,sizeof(tng));
    	while(m--)
    	{
    		int l,r;
    		read(l);read(r);
    		l=(l+x-1)%n+1;
    		r=(r+x-1)%n+1;
    		if(l>r) swap(l,r);
    		x=b[query(l,r)];
    		printf("%d
    ",x);
    	}
    	return 0;
    }
    

    这道题与其说是分块不如说是一道模拟题,按照暴力思路直接优化就不难得到正解

  • 相关阅读:
    246.Strobogrammatic Number
    245.Shortest Word Distance III
    244.Shortest Word Distance II
    243.Shortest Word Distance
    242. Valid Anagram
    241. Different Ways to Add Parentheses
    240.Search in a 2D Matrix II
    239. Sliding Window Maximum
    238. Product of Array Except Self
    postgres 行列转换
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11247093.html
Copyright © 2011-2022 走看看