【题解】 作诗 序列分块 Luogu4135

Legend

给定长度为 \(n\) 的序列 \(a_i\),区间在线询问 \(m\) 次出现次数为偶数的数字个数(0 次不算)。

强制在线。\(1 \le n ,a_i,m \le 10^5\)

Editorial

好像我的做法比较阴间。

考虑分块预处理 \(val_{i,j}\) 表示第 \(i\) 个块到第 \(j\) 个块的答案。

如何处理散块?预处理 \(back_{i,v}\) 表示第 \(i\) 个块及以后数字 \(v\) 从左到右第一次出现的位置。

\(front_{i,v}\) 表示第 \(i\) 个块及以后数字 \(v\) 从右到左第一次出现的位置。

再记录一下 \(where_i\) 每一个位置上的数字是这个数字从左到右第几个。

这样通过查询块内最靠左/右的数字,用 \(where\) 作差,就可以判断出来整块内的数字出现的是偶数还是奇数次了。散块就暴力更新就行了。

复杂度 \(O(n\sqrt{n})\)

然而在写这篇题解的时候发现好像可以直接前缀和求出奇偶性(

Code

#include <bits/stdc++.h>

const int MX = 1e5 + 7;
const int SIZE = 400;

int read(){
	char k = getchar(); int x = 0;
	while(k < '0' || k > '9') k = getchar();
	while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
	return x;
}

int a[MX] ,buc[MX];
int VAL[433][433];
int occb[433][MX];
int occf[433][MX];
// occb[i][j] 表示在块 i 及以后 j 第一次出现的位置
// occf 则是表示之前最后一次出现的位置
int begin[MX] ,end[MX] ,block[MX] ,where[MX];

std::vector<int> app[MX];

int Query(int l ,int r){
	int ans = 0;
	if(block[r] - block[l] <= 1){
		for(int i = l ; i <= r ; ++i){
			buc[a[i]] += 1;
			if((buc[a[i]] & 1) == 0) ++ans;
			else if(buc[a[i]] != 1) --ans;
		}
		for(int i = l ; i <= r ; ++i) buc[a[i]] = 0;
		return ans;
	}
	ans = VAL[block[l] + 1][block[r] - 1];
	for(int i = end[block[l]] ; i >= l ; --i){
		if(buc[a[i]]){
			++buc[a[i]];
			if(buc[a[i]] & 1) --ans;
			else ++ans;
		}
		else{
			int now = where[occf[block[r] - 1][a[i]]] - where[i];
			buc[a[i]] = now + 1;
			if((buc[a[i]] & 1) == 0) ++ans;
			else if(buc[a[i]] != 1) --ans;
		}
	}
	for(int i = begin[block[r]] ; i <= r ; ++i){
		if(buc[a[i]]){
			++buc[a[i]];
			if(buc[a[i]] & 1) --ans;
			else ++ans;
		}
		else{
			int now = where[i] - where[occb[block[l] + 1][a[i]]];
			buc[a[i]] = now + 1;
			if((buc[a[i]] & 1) == 0) ++ans;
			else if(buc[a[i]] != 1) --ans;
		}
	}
	for(int i = l ; i <= end[block[l]] ; ++i) buc[a[i]] = 0;
	for(int i = begin[block[r]] ; i <= r ; ++i) buc[a[i]] = 0;
	return ans;
}

int main(){
	int n = read() ,c = read() ,m = read();
	for(int i = 1 ; i <= n ; ++i){
		a[i] = read();
		where[i] = app[a[i]].size();
		app[a[i]].push_back(i);
		// printf("where[%d] = %d\n" ,i ,where[i]);
	}
	for(int i = 0 ,bl = 0 ; i <= n ; ++i){
		block[i] = bl;
		if(i % SIZE == 0 || i == n){
			end[bl] = i;
			begin[++bl] = i + 1;
		}
	}

	memset(occb ,0x3f ,sizeof occb);
	memset(occf ,0x3f ,sizeof occf);
	for(int i = n ; i ; --i){
		if(i == end[block[i]]){
			for(int j = 0 ; j <= c ; ++j){
				occb[block[i]][j] = occb[block[i] + 1][j];
			}
		}
		occb[block[i]][a[i]] = i;
	}
	for(int i = 1 ; i <= n ; ++i){
		if(i == begin[block[i]]){
			for(int j = 0 ; j <= c ; ++j){
				occf[block[i]][j] = occf[block[i] - 1][j];
			}
		}
		occf[block[i]][a[i]] = i;
	}

	for(int i = 1 ; i <= n ; i += SIZE){
		int ans = 0;
		for(int j = i ; j <= n ; ++j){
			buc[a[j]] += 1;
			if((buc[a[j]] & 1) == 0){
				++ans;
			}
			else if(buc[a[j]] != 1) --ans;
			VAL[block[i]][block[j]] = ans;
		}
		for(int j = i ; j <= n ; ++j) buc[a[j]] = 0;
	}
	int Online = 1 ,la = 0;
	while(m--){
		int l = read() ,r = read();
		if(Online){
			l = (l + la) % n + 1;
			r = (r + la) % n + 1;
			if(l > r) std::swap(l ,r);
		}
		printf("%d\n" ,la = Query(l ,r));
	}
}
posted @ 2020-10-05 20:01  Imakf  阅读(186)  评论(0编辑  收藏  举报