zoukankan      html  css  js  c++  java
  • Violet 6 杯省选模拟赛 蒲公英

    https://www.luogu.com.cn/problem/P4168

    题目

    给$n$个数字,有$m$次询问,问$a_l, a_{l+1} , dots , a_r$的众数是什么,

    $1leqslant n leqslant 40000, 1leqslant m leqslant 50000, 1leqslant a_ileqslant10^9$

    题解

    第一次做分块

    方法一

    因为n不是很大,所以可以对数据进行离散化后统计出现次数

    所以就可以直接统计最大的了。这样复杂度是$mathcal{O}(m imes n)$,肯定超时

    可以尝试提前分块打出一些表,比如分成$t$块,然后提前打好$inom{t}{2}$块的最大值,并保存是哪一个

    那么每次查询的时候最多花$2 imes lfloor n/t floor$的时间,时间复杂度是$mathcal{O}(t^2n+2mn/t)$

    把$m$和$n$看作同数量级,设为N,那么得到$t^2N+2N^2/t$,为了保证数量级相同,设$t^2N=2N^2/t$,得到$t=sqrt[3]{N}$

    因为大于或小于以后两边渐进复杂度都会增加,导致整个表达式的渐进复杂度增加(算法导论:证明$max(a,b)=Theta(a+b)$)

    AC代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #include<vector>
    using namespace std;
    #define REP(i,a,b) for(register int i=(a); i<(b); i++)
    #define REPE(i,a,b) for(register int i=(a); i<=(b); i++)
    #define PERE(i,a,b) for(register int i=(a); i>=(b); i--)
    #ifdef sahdsg
    #define DBG(...) printf(__VA_ARGS__)
    #else
    #define DBG(...) void(0)
    #endif
    typedef long long ll;
    #define MAXN 40007
    #define MAXM 50007
    #define MAXD 35
    int a[MAXN], b[MAXN], c[MAXN];
    int d[MAXD][MAXD][MAXN];
    int now[MAXN];
    int t,l,na;
    int x=0;
    inline void did(int f) {
    	now[f]++;
    	if(now[na]<now[f] || (now[na]==now[f] && now[na+1]>f)) {
    		now[na+1]=f;
    		now[na]=now[f];
    	}
    }
    inline int go(int z, int y) {
    	int i=(z+l-1)/l, j=y/l;
    	int L=i*l, R=j*l;
    	if(i<j) {
    		REP(k,0,na+2)
    			now[k] = d[i][j][k];
    		REP(f,z,L) did(c[f]);
    		REP(f,R,y) did(c[f]);
    	} else {
    		memset(now,0,sizeof now);
    		REP(f,z,y) did(c[f]);
    	}
    	return x=b[now[na+1]];
    }
    int main() {
    	int n,m; scanf("%d%d", &n, &m);
    	REP(i,0,n) {scanf("%d", &a[i]); b[i]=a[i];}
    	sort(b,b+n); na = unique(b,b+n)-b;
    	REP(i,0,n) {
    		c[i] = lower_bound(b,b+na,a[i])-b;
    	}
    	memset(d,0,sizeof d);
    	t = pow((double)n, (double)1/3); 
    	l = t ? n/t : n;
    	REP(i,0,t) REPE(j,i,t) {
    		REP(f,i*l,j*l) {
    			int k = c[f];// DBG("*%d
    ", k);
    			d[i][j][k]++;
    			if(d[i][j][k]>d[i][j][na] || (d[i][j][k]==d[i][j][na] && k<d[i][j][na+1])) {
    				d[i][j][na] = d[i][j][k];
    				d[i][j][na+1] = k;
    			}
    		}
    	}
    
    	REP(i,0,m) {
    		int l0, r0;
    		scanf("%d%d", &l0, &r0);
    		int l = (l0 + x - 1) % n + 1;
    		int r = (r0 + x - 1) % n + 1;
    		if(l>r) swap(l,r);
    		go(l-1,r);
    		printf("%d
    ", x);
    	}
    	return 0;
    }
    

    方法二

    用同样的分块方法,但是只记录最大值,不记录次数,而使用二分确定大小,一次二分确定大小需要$mathcal{O}(log n)$。

    设需要分$D$块,然后得到时间复杂度$mathcal{O}(D imes N+2MN/Dlog n)$(因为剩下部分最长是两个块,虽然比平均情况大,但是为了应付数据,数据是最大的很多……)

    那么用相同的方法,解得$D=sqrt{2Nlog N}$

    中间有个细节,就是计算D和L的时候要考虑是偏大还是篇小,由于我快断电了,所以坑了

    AC代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #include<vector>
    #include<cassert>
    using namespace std;
    #define REP(i,a,b) for(register int i=(a); i<(b); i++)
    #define REPE(i,a,b) for(register int i=(a); i<=(b); i++)
    #define PERE(i,a,b) for(register int i=(a); i>=(b); i--)
    #ifdef sahdsg
    #define DBG(...) printf(__VA_ARGS__)
    #else
    #define DBG(...) void(0)
    #endif
    typedef long long ll;
    #define MAXN 50007
    #define MAXM 50007
    #define MAXL 1007
    #define MAXD 884*2
    int a[MAXN],b[MAXN],c[MAXN],nb;
    int fk[MAXD][MAXD], L, D;
    int cnt[MAXN], nmax, ncnt;
    int x=0;
    vector<int> arr[MAXN];
    int calc(int l, int r, int b) {
    	//r--;
    	return lower_bound(arr[b].begin(), arr[b].end(), r)-lower_bound(arr[b].begin(), arr[b].end(), l);
    }
    void did(int l, int r, int b) {
    	int t=calc(l,r,b);
    	if(t>ncnt || (t==ncnt && b<nmax)) {
    		ncnt=t;
    		nmax=b;
    	}
    }
    void work(int l, int r) {
    	nmax=0, ncnt=0;
    	int z=(l+L-1)/L, y=r/L;
    	if(z<y) {
    		int Z=z*L, Y=y*L;
    		REP(i,l,Z) did(l,r,c[i]);
    		REP(i,Y,r) did(l,r,c[i]);
    		did(l,r,fk[z][y]);
    	} else {
    		REP(i,l,r) did(l,r,c[i]);
    	}
    	x=b[nmax];
    }
    int main() {
    	int n,m; scanf("%d%d", &n, &m);
    	REP(i,0,n) {
    		scanf("%d", &a[i]);
    		b[i]=a[i];
    	}
    	sort(b,b+n);
    	nb = unique(b,b+n)-b;
    	REP(i,0,n) {
    		c[i] = lower_bound(b,b+nb,a[i])-b;
    	}
    	D = sqrt(log((double)n)/log(2.0)*n*2); if(D==0) D=1;
    	L = n/D; //L<L' D>D'
    	D = n/L; //L>L' D<D'
    	REP(i,0,D) {
    		int s=i*L;
    		nmax = 0, ncnt = 0;
    		REP(k,0,n) cnt[k]=0;
    		REP(j,s,n) {
    			int J=(j+1+L-1)/L, k=c[j];
    			cnt[k]++;
    			if(cnt[k]>ncnt ||(cnt[k]==ncnt && k<nmax)) {
    				ncnt = cnt[k];
    				nmax = k;
    			}
    			fk[i][J]=nmax;
    		}
    	}
    	REP(i,0,n) {
    		arr[c[i]].push_back(i);
    	}
    	REP(i,0,m) {
    		int l,r;
    		scanf("%d%d", &l, &r);
    		l = (l+x-1)%n+1, r=(r+x-1)%n+1;
    		if(l>r) swap(l,r);
    		work(l-1,r);
    		printf("%d
    ", x);
    	}
    }
    
  • 相关阅读:
    bzoj3110
    idea 设置系列 各种乱码
    vim 系列
    idea 神键
    简单工厂,工厂方法,抽象工厂
    log4 按包进行日志输出
    maven依赖本地宝
    kafka 理论学习
    kafka windows环境搭建 测试
    linux 查找文件的命令
  • 原文地址:https://www.cnblogs.com/sahdsg/p/12057124.html
Copyright © 2011-2022 走看看