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('
    ');
    	}
    }
    
  • 相关阅读:
    MySQL效能监控工具mysqlreport安装和中文说明
    mysql的配置文件my.cnf
    MySQL的高可用设计方案的记录
    jQuery 求页面加载等待特效
    如果客户端禁用cookie,session还能使用吗?
    heartbeat重要文件的配置参数说明
    MySQL的索引类型和左前缀索引
    比较一下Linux下的Epoll模型和select模型的区别
    网络防火墙和NAT地址转换
    Postfix+dovecot搭建简单邮箱服务器
  • 原文地址:https://www.cnblogs.com/birchtree/p/10413710.html
Copyright © 2011-2022 走看看