bzoj入门4839 蒲公英

题目:

n个数m个询问,每个询问是一个[l,r],输出[l,r]之前的众数。有相同的输出小的。n <=40000,m<=50000,ai<=1e9

分析:

分块。
预处理出f[i][j]表示第i个块到第j个块的众数。对于一个询问[l,r],中间整块的部分可以直接得到答案ans,然后对于两边的2*sqrt(n)个元素,查询出每个元素的出现次数,看是否能更新ans。至于,查找一个[l,r]内x的出现次数,可通过记录x的每个出现位置,二分上下界得到。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ms(a,b) memset(a,b,sizeof(a))
#define lson rt*2,l,(l+r)/2
#define rson rt*2+1,(l+r)/2+1,r
typedef unsigned long long ull;
typedef long long ll;
const int MAXN = 4e4 + 5;
const double EPS = 1e-8;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int read(){
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x;
}
map<int, int> mp;
int v[MAXN], val[MAXN], id, cnt[MAXN], bl[MAXN],f[905][905],blo,n,m;
int first[MAXN],last[MAXN];
struct data{int v,p;}b[MAXN];
bool operator <(data a, data b) {
	return a.v < b.v || (a.v == b.v && a.p < b.p);
}
void pre(int x) {
	for(int i=1;i<=id;i++)	cnt[i] = 0;
	int mx = 0, ans = 0;
	for (int i = (x - 1) * blo + 1; i <= n; i++) {
		cnt[v[i]]++;
		int t = bl[i];
		if (cnt[v[i]] > mx || (cnt[v[i]] == mx && val[v[i]] < val[ans])) {
			mx = cnt[v[i]];
			ans = v[i];
		}
		f[x][t] = ans;
	}
}
int findup(int x,int v){
	int l = first[v], r = last[v];
	int tmp = 0;
	while(l<=r){
		int mid = (l+r)>>1;
		if(x < b[mid].p)	r = mid - 1;
		else {l=mid+1;tmp=mid;}
	}
	return tmp;
}
int finddown(int x,int v){
	int l = first[v], r = last[v];
	int tmp = INF;
	while(l<=r){
		int mid = (l+r)>>1;
		if(x>b[mid].p)	l=mid+1;
		else{r=mid-1;tmp=mid;}
	}
	return tmp;
}
int find(int x,int y,int v){
	return max(0,findup(y,v)-finddown(x,v)+1);
}
int query(int a, int b) {
	int ans, mx;
	ans = f[bl[a] + 1][bl[b] - 1];
	mx = find(a, b, ans);
	for (int i = a; i <= min(bl[a]*blo, b); i++) {
		int t = find(a,b,v[i]);
		if(mx < t || (mx == t && val[v[i]] < val[ans])){
			ans = v[i];
			mx = t;
		}
	}
	if(bl[a] != bl[b]){
		for(int i=(bl[b]-1)*blo+1;i<=b;i++){
			int t = find(a,b,v[i]);
			if(mx < t || (mx == t && val[v[i]] < val[ans])){
				ans = v[i];
				mx = t;
			}
		}
	}
	return ans;
}
int main() {
	ios::sync_with_stdio(false);
	n = read();	m = read();
	blo = sqrt((double)n / log((double)n) * log(2));
	for (int i = 1; i <= n; i++) {
		bl[i] = (i - 1) / blo + 1;
		v[i] = read();
		if (!mp.count(v[i])) {
			mp[v[i]] = ++id;
			val[id] = v[i];
		}
		v[i] = mp[v[i]];
		b[i].p = i, b[i].v = v[i];
	}
	sort(b + 1, b + n + 1);
	for (int i = 1; i <= n; i++) {
		if (!first[b[i].v])	first[b[i].v] = i;
		last[b[i].v] = i;
	}
	for (int i = 1; i <= bl[n]; i++)	pre(i);
	int ans = 0;
	while(m--) {
		int a, b;
		a = read();	b = read();
		a = (a + ans - 1) % n + 1, b = (b + ans - 1) % n + 1;
		if (a > b)	swap(a, b);
		ans = val[query(a, b)];
		printf("%d\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值