1. 程式人生 > >作詩 --- 分塊(區間眾數)

作詩 --- 分塊(區間眾數)

題目描述

神犇SJY虐完HEOI之後給傻×LYD出了一題:

SHY是T國的公主,平時的一大愛好是作詩。

由於時間緊迫,SHY作完詩之後還要虐OI,於是SHY找來一篇長度為N的文章,閱讀M次,每次只閱讀其中連續的一段[l,r],從這一段中選出一些漢字構成詩。因為SHY喜歡對偶,所以SHY規定最後選出的每個漢字都必須在[l,r]裡出現了正偶數次。而且SHY認為選出的漢字的種類數(兩個一樣的漢字稱為同一種)越多越好(為了拿到更多的素材!)。於是SHY請LYD安排選法。

LYD這種傻×當然不會了,於是向你請教……

問題簡述:N個數,M組詢問,每次問[l,r]中有多少個數出現正偶數次。

分析

  與求區間眾數差不多吧([Violet]蒲公英)
  1.先預處理處塊內每個種類的個數,再字首和處理一下
  2.求出塊與塊之間的ans
  3.對於詢問:只要處理非完整塊中的種類即可(完整塊中的提前加上,到時候判斷即可)

程式碼寫得太醜,常數有點大,望大佬指教。

程式碼

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>

#define IL inline
#define open(s) freopen(s".in","r",stdin); freopen(s".out","w",stdout);
#define close fclose(stdin); fclose(stdout); using namespace std; IL int read() { char c = getchar(); int sum = 0 ,k = 1; for(;'0' > c || c > '9'; c = getchar()) if(c == '-') k = -1; for(;'0' <= c && c <= '9'; c = getchar()) sum = sum * 10 + c - '0'; return
sum * k; } const int maxn = 100000 + 5, maxT = 320; int n, k, m, T; int block; int num[maxn]; int cnt[maxT][maxn]; int Ans[maxT][maxT]; int use[maxn]; struct Block { int l, r; }ip[maxT]; IL void add(int w, int &val) { if(w && !(w & 1)) ++val; if(w > 1 && (w & 1)) --val; } IL void pre_work() { for(int i = 2; i < T; ++i) for(int j = 1; j <= k; ++j) cnt[i][j] += cnt[i - 1][j]; // 字首和 for(int i = 1, s, an; i < T; ++i) { an = 0; for(int j = i; j < T; ++j) { for(int l = ip[j].l; l <= ip[j].r; ++l) //列舉這個塊上的種類就夠了, 不用列舉全部種類 add(++use[num[l]], an); Ans[i][j] = an; } for(int j = ip[i].l; j <= n; ++j) if(use[num[j]]) use[num[j]] = 0; } } IL int query(int x, int y) { int l = x / block + 1, r = y / block + 1, ans; if(l + 1 >= r) { ans = 0; for(int i = x, s; i <= y; ++i) add(++use[num[i]], ans); for(int i = x; i <= y; ++i) if(use[num[i]]) use[num[i]] = 0; }else { ans = Ans[l + 1][r - 1]; for(int i = x; i <= ip[l].r; ++i) { if(!use[num[i]]) use[num[i]] = cnt[r - 1][num[i]] - cnt[l][num[i]]; add(++use[num[i]], ans); } for(int i = ip[r].l; i <= y; ++i) { if(!use[num[i]]) use[num[i]] = cnt[r - 1][num[i]] - cnt[l][num[i]]; add(++use[num[i]], ans); } for(int i = x; i <= ip[l].r; ++i) if(use[num[i]]) use[num[i]] = 0; for(int i = ip[r].l; i <= y; ++i) if(use[num[i]]) use[num[i]] = 0; } return ans; } int main() { open("4135") n = read(); k = read(); m = read(); block = sqrt( (double)n / log((double)n) * log(2)); T = 1; for(int i = 1; i <= n; ++i) { num[i] = read(); ++cnt[T][num[i]]; if(!(i % block) || i == n) { ip[T].r = i; ip[++T].l = i + 1; } } pre_work(); for(int pre = 0, x, y; m; --m) { x = (read() + pre) % n + 1; y = (read() + pre) % n + 1; if(x > y) swap(x, y); printf("%d\n", pre = query(x, y)); } close return 0; }