bzoj 3489: A simple rmq problem

传送门.

题解:

本来应该用数据结构大力解决这题,无非就是对于每一个点求一个next、last分别表示上一个和下一个和它相同的,然后变成了三维偏序问题。

然后随便想了个分块,考虑不仅对序列分块,还对值域分块。

s i , j , k s_{i,j,k} si,j,k表示序列中的第i块到第j块在值域第k块的出现次数为1的个数。
再维护个前缀和 p i , j p_{i,j} pi,j表示序列前i块在j的个数。

然后就可以快速确定答案在不在一个块里,如果在,进去暴力即可。

发现被卡常了,然后就要……

Code:

#pragma GCC optimize(2)
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;

const int N = 1e5 + 5, M = 240, C = N / M + 5;

int n, m, a[N];
int l[N], r[N], bl[N];
int b[C][N], s[C][C][C], li[C], cnt[N];
int x, y, ans;

int main() {
	scanf("%d %d", &n, &m);
	fo(i, 1, n) {
		bl[i] = bl[i - 1];
		if(i % M == 1) r[bl[i]] = i - 1, l[++ bl[i]] = i;
	} r[bl[n]] = n;
	fo(i, 1, n) scanf("%d", &a[i]);
	fo(i, 1, bl[n]) {
		fo(j, 1, n) cnt[j] = 0;
		fo(j, i, bl[n]) {
			memcpy(s[i][j], s[i][j - 1], sizeof s[i][j]);
			fo(k, l[j], r[j])
				if(++ cnt[a[k]] == 1) s[i][j][bl[a[k]]] ++; else
				s[i][j][bl[a[k]]] -= cnt[a[k]] == 2;
		}
	}
	fo(i, 1, bl[n]) {
		memcpy(b[i], b[i - 1], sizeof b[i]);
		fo(j, l[i], r[i]) b[i][a[j]] ++;
	}
	fo(j, 1, n) cnt[j] = 0; fo(j, 1, bl[n]) li[j] = 0;
	fo(ii, 1, m) {
		scanf("%d %d", &x, &y);
		x = (x + ans) % n + 1;
		y = (y + ans) % n + 1;
		if(x > y) swap(x, y);
		ans = 0;
		int p = bl[x] + 1, q = bl[y] - 1;
		int tx, ty;
		if(bl[x] == bl[y])
			tx = y, ty = y + 1; else
			tx = r[bl[x]], ty = l[bl[y]];
		int ex = 0, ey = 0;
		if(bl[x] != bl[y]) {
			if(tx - x > M / 2) {
				p --;
				tx = l[bl[x]];
				x --;
				ex = 1;
			}
			if(y - ty > M / 2) {
				q ++;
				ty = r[bl[y]];
				y ++;
				ey = 1;
			}
		}
		if(p > q) q = p - 1;
		fo(i, 1, bl[n]) li[i] = s[p][q][i];
		if(!ex) {
			fo(i, x, tx) {
				cnt[a[i]] ++; int w = b[q][a[i]] - b[p - 1][a[i]];
				li[bl[a[i]]] += (cnt[a[i]] + w) == 1;
				li[bl[a[i]]] -= (cnt[a[i]] + w) == 2;
			}
		} else {
			fo(i, tx, x) {
				cnt[a[i]] --; int w = b[q][a[i]] - b[p - 1][a[i]];
				li[bl[a[i]]] += (cnt[a[i]] + w) == 1;
				li[bl[a[i]]] -= (cnt[a[i]] + w) == 0;
			}
		}
		if(!ey) {
			fo(i, ty, y) {
				cnt[a[i]] ++; int w = b[q][a[i]] - b[p - 1][a[i]];
				li[bl[a[i]]] += (cnt[a[i]] + w) == 1;
				li[bl[a[i]]] -= (cnt[a[i]] + w) == 2;
			}
		} else {
			fo(i, y, ty) {
				cnt[a[i]] --; int w = b[q][a[i]] - b[p - 1][a[i]];
				li[bl[a[i]]] += (cnt[a[i]] + w) == 1;
				li[bl[a[i]]] -= (cnt[a[i]] + w) == 0;
			}
		}
		fd(i, bl[n], 1) if(li[i]) {
			fd(j, r[i], l[i])
				if(cnt[j] + b[q][j] - b[p - 1][j] == 1) {
					ans = j; break;
				}
			break;
		}
		fo(i, 1, bl[n]) li[i] = 0;
		if(!ex) fo(i, x, tx) cnt[a[i]] = 0; else fo(i, tx, x) cnt[a[i]] = 0;
		if(!ey) fo(i, ty, y) cnt[a[i]] = 0; else fo(i, y, ty) cnt[a[i]] = 0;
		printf("%d\n", ans);
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值