zoukankan      html  css  js  c++  java
  • BZOJ 2724蒲公英 (分块) 【内有块大小证明】

    题面

    luogu传送门

    分析

    先分块,设块大小为x(之后我们会证明块大小取何值会更优)

    步骤1

    把所有的数离散化,然后对每个值开一个vector pos[i],pos[i]存储数i出现的位置

    我们设查询的区间为[l,r],需要求数v出现的次数,然后在vector中二分查找出第一个>=l的数的位置p1,和第一个>r的数的位置p2,p2-p1即为数v出现的次数

    例:
    离散化后的数组a={1,3,3,2,3,1,3 },则pos[3]={2,3,5,7},因为第2,3,5个数为3
    
    我们需要查找[2,6]中数3出现的次数,发现p1=2,p2=4,出现的次数即为2
    

    步骤2

    然后我们考虑询问,如果只记录每个块里的众数显然是不行的,因为我们需要把许多个块的结果合起来,而众数不满足区间可加性,无法在(O(sqrt n))的时间内完成结果合并

    因此我们预处理出所有块端点组成的区间,(mode[l][r],maxt[l][r]),表示[第l个块的起点,第r个块的终点]这个区间里的众数和众数的出现次数

    查询[l,r]时我们可以得到中间的一个由块端点组成的区间,可以直接通过刚刚的预处理得到众数和众数的出现次数

    两边多余的部分直接用步骤1暴力即可

    至于mode,maxt数组如何预处理,直接用两重for循环来实现

    首先枚举块的起点i,然后j从i遍历到n,用一个临时数组cnt来记录每个数出现的次数,就可以求出区间[i,j]的众数,如果j正好是块端点,则记录答案

    for(int i=1; i<=bl; i++) {
    		int ans=INF;
    		int tim=0;
    		for(int j=lb(i); j<=n; j++) {
    			cnt[a[j]]++;
    			if(cnt[a[j]]>tim||(cnt[a[j]]==tim&&a[j]<ans)) {
    				ans=a[j];
    				tim=cnt[a[j]];
    			}
    			if(j%sz==0) {
    				mode[i][j/sz]=ans;
    				maxt[i][j/sz]=tim;
    			}else if(j==n){
    				mode[i][bl]=ans;
    				maxt[i][bl]=tim;
    			}
    		}
    		for(int j=lb(i); j<=n; j++) cnt[a[j]]=0;
    	}
    

    时间复杂度分析

    设块的大小为x,假设n为x的倍数

    初始化部分:

    从第一个块末尾需要遍历n-x次,从第二个块末尾需要遍历n-2x次,从第(frac{n}{x})个块末尾需要遍历(n-x·frac{n}{x})

    总的遍历次数为

    [(n-x)+(n-2x)+dots+n-x·frac{n}{x}=frac{n^2}{2x}-frac{1}{2}n ]

    其中(frac{1}{2}n)可忽略,时间复杂度为(O(frac{n^2}{x}))

    查询部分:

    考虑查询的极端情况,查询[2,n-1]

    则需要遍历左,右长度各为(x-1)的块(可近似看成x)

    时间复杂度为(O(x log n))

    m次询问(O(mx log n))

    所以总时间复杂度为(O(frac{n^2}{x}+mx log n))

    根据均值不等式

    (x=sqrt{frac{n^2}{m log n}})时,总时间复杂度为(2sqrt{n^2mlog n}=O(nsqrt{mlog n}))

    因此块大小为(sqrt{frac{n^2}{m log n}})时最优,由于n,m同级,可近似取(sqrt{n log n})

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<map>
    #include<cmath>
    #include<vector>
    #define maxn 100005
    #define maxs 2005
    #define INF 0x7fffffff 
    using namespace std;
    inline void qread(int &x) {
    	x=0;
    	int sign=1;
    	char c=getchar();
    	while(c<'0'||c>'9') {
    		if(c=='-') sign=-1;
    		c=getchar();
    	}
    	while(c>='0'&&c<='9') {
    		x=x*10+c-'0';
    		c=getchar();
    	}
    	x=x*sign;
    }
    inline void qread(long long &x) {
    	x=0;
    	long long sign=1;
    	char c=getchar();
    	while(c<'0'||c>'9') {
    		if(c=='-') sign=-1;
    		c=getchar();
    	}
    	while(c>='0'&&c<='9') {
    		x=x*10+c-'0';
    		c=getchar();
    	}
    	x=x*sign;
    }
    inline void qprint(int x) {
    	if(x<0) {
    		putchar('-');
    		qprint(-x);
    	} else if(x==0) {
    		putchar('0');
    		return;
    	} else {
    		if(x/10>0) qprint(x/10);
    		putchar('0'+x%10);
    	}
    }
    
    int n,m,num,sz,bl;
    int id[maxn];//第i个位置属于的块编号
    int a[maxn];
    int b[maxn];
    inline int lb(int id) {//求第id个块的左端点
    	return sz*(id-1)+1;
    }
    inline int rb(int id) {//求第id个块的右端点
    	return sz*id>n?n:sz*id;
    }
    
    vector<int>pos[maxn];
    int get_count(int val,int l,int r) {
    	return upper_bound(pos[val].begin(),pos[val].end(),r)-lower_bound(pos[val].begin(),pos[val].end(),l);
    }
    
    int cnt[maxn];
    int mode[maxs][maxs];
    int maxt[maxs][maxs];
    void ini() {
    	for(int i=1; i<=n; i++) {
    		pos[a[i]].push_back(i);
    	}
    //	for(int i=1; i<=num; i++) {
    //		sort(pos[b[i]].begin(),pos[b[i]].end());
    //	}
    	for(int i=1; i<=bl; i++) {
    		int ans=INF;
    		int tim=0;
    		for(int j=lb(i); j<=n; j++) {
    			cnt[a[j]]++;
    			if(cnt[a[j]]>tim||(cnt[a[j]]==tim&&a[j]<ans)) {
    				ans=a[j];
    				tim=cnt[a[j]];
    			}
    			if(j%sz==0) {
    				mode[i][j/sz]=ans;
    				maxt[i][j/sz]=tim;
    			}else if(j==n){
    				mode[i][bl]=ans;
    				maxt[i][bl]=tim;
    			}
    		}
    		for(int j=lb(i); j<=n; j++) cnt[a[j]]=0;
    	}
    }
    
    int query(int l,int r) {
    	int ans=INF;
    	int tim=0;
    	if(id[l]+1<=id[r]-1) {
    		ans=mode[id[l]+1][id[r]-1];
    		tim=maxt[id[l]+1][id[r]-1];
    	}
    	for(int i=l; i<=min(r,rb(id[l])); i++) {
    		int tmp=get_count(a[i],l,r);
    		if(tmp>tim||(tmp==tim&&a[i]<ans)) {
    			ans=a[i];
    			tim=tmp;
    		}
    	}
    	if(id[l]!=id[r]) {
    		for(int i=lb(id[r]); i<=r; i++) {
    			int tmp=get_count(a[i],l,r);
    			if(tmp>tim||(tmp==tim&&a[i]<ans)) {
    				ans=a[i];
    				tim=tmp;
    			}
    		}
    	}
    //	return ans;
    	return b[ans];
    }
    
    int main() {
    	int l,r;
    	qread(n);
    	qread(m);
    	for(int i=1; i<=n; i++) {
    		qread(a[i]);
    		b[i]=a[i];
    	}
    	sort(b+1,b+1+n);
    	num=unique(b+1,b+1+n)-b-1;
    	for(int i=1; i<=n; i++) {
    		a[i]=lower_bound(b+1,b+1+num,a[i])-b;
    	}
    	sz=sqrt(n/(log(n)/log(2)));
    	bl=1;
    	for(int i=1; i<=n; i++) {
    		id[i]=bl;
    		if(i%sz==0) bl++;
    	}
    	int x=0;
    	ini();
    	for(int i=1; i<=m; i++) {
    		qread(l);
    		qread(r);
    		l=(l+x-1)%n+1;
    		r=(r+x-1)%n+1;
    		if(l>r) swap(l,r);
    		x=query(l,r);
    		qprint(x);
    		putchar('
    ');
    	}
    }
    
  • 相关阅读:
    ZeptoLab Code Rush 2015
    UVa 10048 Audiophobia【Floyd】
    POJ 1847 Tram【Floyd】
    UVa 247 Calling Circles【传递闭包】
    UVa 1395 Slim Span【最小生成树】
    HDU 4006 The kth great number【优先队列】
    UVa 674 Coin Change【记忆化搜索】
    UVa 10285 Longest Run on a Snowboard【记忆化搜索】
    【NOIP2016提高A组模拟9.28】求导
    【NOIP2012模拟10.9】电费结算
  • 原文地址:https://www.cnblogs.com/birchtree/p/10413710.html
Copyright © 2011-2022 走看看