断断续续调了5天,没搞定。。。打表的部分应该没错。第一个调出来的我请饮料一杯任选!

#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
// 分块代码的循环太多了,建议搞个宏
#define _for(a, b) for(int i=a; i<=b; i++)
#define _for_j(a, b) for(int j=a; j<=b; j++)

const int N = 4e4 + 10, SQRT_N = 2e2 + 10;
int L[SQRT_N], R[SQRT_N], pos[N];
int n, nblock; // 元素个数,分块的块数
int a[N], t[N]; // 原数组,离散化中间结果数组
int b[N]; // 离散化后的结果(排名)

// 离散化
void disc() {
	sort(t + 1, t + 1 + n);
	int num = unique(t + 1, t + 1 + n) - (t + 1);
	_for(1, n)
	b[i] = lower_bound(t + 1, t + 1 + num, a[i]) - t;
}

// 初始化分块相关的量
void init() {
	int t = sqrt(n); // 块的大小
	nblock = n / t; // 块数
	if (n % t) nblock++;
	// 求出每一块的左右端点
	_for(1, nblock) {
		L[i] = (i - 1) * t + 1;
		R[i] = i * t;
	}
	// 防止 n%t!=0 时出现错误
	R[nblock] = n;

	// 求出原数组每个元素分在哪一块
	_for(1, nblock) _for_j(L[i], R[i]) pos[j] = i;
}

// 第i块到第j块内的最小众数的编号!编号!编号!
int mx[SQRT_N][SQRT_N];
// 从第1块到第i块,(离散化后的)数字j出现的次数
int cnt[SQRT_N][N];
// 预处理分块统计的中间结果
void pre() {
	_for(1, nblock) {
		// 要把cnt[i]行的每一个数都复制到下一行
		_for_j(1, n) cnt[i][b[j]] = cnt[i - 1][b[j]];
		// 然后才是统计出现的数字
		_for_j(L[i], R[i]) cnt[i][b[j]]++;
		// 以上两个过程反过来也可以的,当然要注意用+=
	}

	// 统计第i~j块的众数
	_for(1, nblock) _for_j(i, nblock) {
		// val初始化为1表示块i的第一个数出现1次
		int idx = L[i], val = 1;
		// 从第i块的左端点到第j块的右端点
		for (int j2 = L[i]; j2 <= R[j]; j2++) {
			int e = 0;
			// mx[i][i](每一块)的众数要单独处理
			if (i == j) e = cnt[j][b[j2]] - cnt[j - 1][b[j2]];
			// b[j2]在第i~j块内出现的次数
			else e = cnt[j][b[j2]] - cnt[j - i - 1][b[j2]];
			if (e > val || (e == val && b[j2] < b[idx])) {
				val = e;
				idx = j2;
			}
		}
		mx[i][j] = idx;
	}
}

void debug() {
	cout << "\n\nb:\n";
	_for(1, 16) cout << b[i] << ' ';

	cout << "\ncnt:\n";
	_for(1, 4) {
		_for_j(1, 16) cout << cnt[i][j] << ' ';
		cout << '\n';
	}
	cout << "a[mx[i][j]]:\n";
	_for(1, 4) {
		_for_j(1, 4) cout << a[mx[i][j]] << ' ';
		cout << '\n';
	}
	cout << '\n';
}

// 暂存统计结果
int tmp[N];
// 返回值:众数的索引
int query(int x, int y) {
	// 先清空本次查询要用到的桶
	_for(x, y) tmp[b[i]] = 0;

	int p = pos[x], q = pos[y];
	if (q - p <= 1) {
		_for(x, y) tmp[b[i]]++;
		int key = x;
		_for(x, y)
		if ((tmp[b[i]] == tmp[b[key]] && b[i] < b[key])
		    || tmp[b[i]] > tmp[b[key]])
			key = i;
		return key;
	}

	// 左端
	_for(x, R[p]) tmp[b[i]]++;
	// 右端
	_for(L[q], y) tmp[b[i]]++;
	// 对于头、尾两段里出现的每一个数,求出次数最多的那个数
	int key = x, maxval = 0;
	_for(x, R[p]) {
		int tot = tmp[b[i]] + cnt[q - 1][b[i]] - cnt[p][b[i]];
		if ((tot == maxval && b[i] < b[key])
		    || tot > maxval)
			key = i, maxval = tot;
	}
	_for(L[q], y) {
		int tot = tmp[b[i]] + cnt[q - 1][b[i]] - cnt[p][b[i]];
		if ((tot == maxval && b[i] < b[key])
		    || tot > maxval)
			key = i, maxval = tot;
	}
	// 再与 [x,y] 的区间众数(加头尾出现次数)进行比较
	// 众数(离散化后)
	int bmax = b[mx[p + 1][q - 1]];
	// 众数在 [x,y] 内出现的次数
	int tot = tmp[bmax] + cnt[q - 1][bmax] - cnt[p][bmax];
	if ((maxval == tot && bmax < b[key]) || maxval < tot)
		key = mx[p + 1][q - 1];
	return key;
}

int main() {
	freopen("P4168_1.in", "r", stdin);
	//freopen("test.in", "w", stdout);
	int m;
	cin >> n >> m;
	_for(1, n) cin >> a[i], t[i] = a[i];
	disc();
	init();
	pre();
//	debug();

	int x = 0;
	while (m--) {
		int l0, r0, l1, r1, l2, r2;
		cin >> l0 >> r0;
		l1 = (l0 + x - 1) % n + 1;
		r1 = (r0 + x - 1) % n + 1;
		l2 = min(l1, r1), r2 = max(l1, r1);
		// 调试用
//		l2 = l0, r2 = r0;

		// 查询区间众数的索引
		x = query(l2, r2);
		cout << a[x] << '\n';
	}

	return 0;
}

1 comments

  • @ 2025-1-7 13:40:00

    AC了,饮料,please!

    #include <iostream>
    #include <cmath>
    #include <algorithm>
    #include <cstring>
    #include <map>
    using namespace std;
    // 分块代码的循环太多了,建议搞个宏
    #define _for(a, b) for(int i=a; i<=b; i++)
    #define _for_j(a, b) for(int j=a; j<=b; j++)
    
    const int N = 4e4 + 10, SQRT_N = 2e2 + 10;
    int L[SQRT_N], R[SQRT_N], pos[N];
    int n, nblock; // 元素个数,分块的块数
    int a[N], t[N]; // 原数组,离散化中间结果数组
    int b[N]; // 离散化后的结果(排名)
    
    // 离散化
    void disc() {
    	sort(t + 1, t + 1 + n);
    	int num = unique(t + 1, t + 1 + n) - (t + 1);
    	_for(1, n)
    	b[i] = lower_bound(t + 1, t + 1 + num, a[i]) - t;
    }
    
    // 初始化分块相关的量
    void init() {
    	int t = sqrt(n); // 块的大小
    	nblock = n / t; // 块数
    	if (n % t) nblock++;
    	// 求出每一块的左右端点
    	_for(1, nblock) {
    		L[i] = (i - 1) * t + 1;
    		R[i] = i * t;
    	}
    	// 防止 n%t!=0 时出现错误
    	R[nblock] = n;
    
    	// 求出原数组每个元素分在哪一块
    	_for(1, nblock) _for_j(L[i], R[i]) pos[j] = i;
    }
    
    // 第i块到第j块内的最小众数的编号!编号!编号!
    int mx[SQRT_N][SQRT_N];
    // 从第1块到第i块,(离散化后的)数字j出现的次数
    int cnt[SQRT_N][N];
    // 预处理分块统计的中间结果
    void pre() {
    	_for(1, nblock) {
    		// 要把cnt[i]行的每一个数都复制到下一行
    		_for_j(1, n) cnt[i][b[j]] = cnt[i - 1][b[j]];
    		// 然后才是统计出现的数字
    		_for_j(L[i], R[i]) cnt[i][b[j]]++;
    		// 以上两个过程反过来也可以的,当然要注意用+=
    	}
    
    	// 统计第i~j块的众数
    	_for(1, nblock) _for_j(i, nblock) {
    		// val初始化为1表示块i的第一个数出现1次
    		int idx = L[i], val = 1;
    		// 从第i块的左端点到第j块的右端点
    		for (int j2 = L[i]; j2 <= R[j]; j2++) {
    			int e = 0;
    			// mx[i][i](每一块)的众数要单独处理
    			e = cnt[j][b[j2]] - cnt[i - 1][b[j2]];//j-i-1->i-1,前缀和
    			if (e > val || (e == val && b[j2] < b[idx])) {
    				val = e;
    				idx = j2;
    			}
    		}
    		mx[i][j] = idx;
    	}
    }
    
    void debug() {
    	cout << "\n\nb:\n";
    	_for(1, 16) cout << b[i] << ' ';
    
    	cout << "\ncnt:\n";
    	_for(1, 4) {
    		_for_j(1, 16) cout << cnt[i][j] << ' ';
    		cout << '\n';
    	}
    	cout << "a[mx[i][j]]:\n";
    	_for(1, 4) {
    		_for_j(1, 4) cout << a[mx[i][j]] << ' ';
    		cout << '\n';
    	}
    	cout << '\n';
    }
    
    // 暂存统计结果
    int tmp[N];
    // 返回值:众数的索引
    int query(int x, int y) {
    	// 先清空本次查询要用到的桶
    	_for(x, y) tmp[b[i]] = 0;
    
    	int p = pos[x], q = pos[y];
    	if (q - p <= 1) {
    		_for(x, y) tmp[b[i]]++;
    		int key = x;
    		_for(x, y)
    		if ((tmp[b[i]] == tmp[b[key]] && b[i] < b[key])
    		    || tmp[b[i]] > tmp[b[key]])
    			key = i;
    		return key;
    	}
    
    	// 左端
    	_for(x, R[p]) tmp[b[i]]++;
    	// 右端
    	_for(L[q], y) tmp[b[i]]++;
    	// 对于头、尾两段里出现的每一个数,求出次数最多的那个数
    	int key = x, maxval = 0;
    	_for(x, R[p]) {
    		int tot = tmp[b[i]] + cnt[q - 1][b[i]] - cnt[p][b[i]];
    		if ((tot == maxval && b[i] < b[key])
    		    || tot > maxval)
    			key = i, maxval = tot;
    	}
    	_for(L[q], y) {
    		int tot = tmp[b[i]] + cnt[q - 1][b[i]] - cnt[p][b[i]];
    		if ((tot == maxval && b[i] < b[key])
    		    || tot > maxval)
    			key = i, maxval = tot;
    	}
    	// 再与 [x,y] 的区间众数(加头尾出现次数)进行比较
    	// 众数(离散化后)
    	int bmax = b[mx[p + 1][q - 1]];
    	// 众数在 [x,y] 内出现的次数
    	int tot = tmp[bmax] + cnt[q - 1][bmax] - cnt[p][bmax];
    	if ((maxval == tot && bmax < b[key]) || maxval < tot)
    		key = mx[p + 1][q - 1];
    	return key;
    }
    
    int main() {
    //	freopen("P4168_1.in", "r", stdin);
    //	freopen("test.in", "w", stdout);
    	int m;
    	cin >> n >> m;
    	_for(1, n) cin >> a[i], t[i] = a[i];
    	disc();
    	init();
    	pre();
    //	debug();
    
    	int x = 0;
    	while (m--) {
    		int l0, r0, l1, r1, l2, r2;
    		cin >> l0 >> r0;
    		l1 = (l0 + x - 1) % n + 1;
    		r1 = (r0 + x - 1) % n + 1;
    		l2 = min(l1, r1), r2 = max(l1, r1);
    		// 调试用
    //		l2 = l0, r2 = r0;
    
    		// 查询区间众数的索引
    		x = query(l2, r2);
    		cout << (x = a[x]) << '\n';//x->(x=a[x])
    		//哈哈哈 看题
    	}
    
    	return 0;
    }
    
    
    • @ 2025-1-7 13:43:19

      就是

      1. 第64行前缀和
      2. 第159~160行输出
    • @ 2025-1-7 14:10:42

      @

      👍👍👍

      我断断续续写了几天,写到后面脑子就糨糊了。想不到输出那么恶心!!!

      饮料任选啊【手动狗头

  • 1

Information

ID
284
Time
1000ms
Memory
256MiB
Difficulty
9
Tags
# Submissions
37
Accepted
2
Uploaded By