BZOJ 2821 作诗

Link

sro orz

Solution

预处理出块与块之间的答案,预处理出每个数字在前ii块的出现次数 然后对于零散部分遍历,记录每个数字的出现次数,利用整块的某个数字的奇偶性判断对答案的贡献+1/-1。

Tips

似乎序列分块的大体套路就是处理块与块的答案和想办法快速地计算零碎部分对答案的贡献?

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include "lucida"
using std::swap;
using std::partial_sum;
const int MAXN=1e5+5,MAXB=320;
struct MagicArray {
int a[MAXN],us[MAXN],ut;
void Reset() {
++ut;
}
int &operator [](int x) {
if(us[x]!=ut) {
us[x]=ut;
a[x]=0;
}
return a[x];
}
}counter;
int a[MAXN],n,bsz,bc,bn[MAXN],bhe[MAXN],bAns[MAXB][MAXB],cext[MAXN][MAXB];
inline bool valid(int x) {
return x && (!(x&1));
}
int Query(int l,int r) {
counter.Reset();
int res=0,c;
if(bn[r]-bn[l]<=1) {
for(int i=l;i<=r;++i) {
if(valid(c=++counter[a[i]])) res++;
else if(c!=1) res--;
}
} else {
int lbo=bn[l]+1,rbo=bn[r]-1;
res=bAns[lbo][rbo];
for(int i=l;i<bhe[lbo];++i)
if(valid(c=(++counter[a[i]]+cext[a[i]][rbo]-cext[a[i]][lbo-1]))) res++;
else if(c!=1) res--;
for(int i=bhe[bn[r]];i<=r;++i)
if(valid(c=(++counter[a[i]]+cext[a[i]][rbo]-cext[a[i]][lbo-1]))) res++;
else if(c!=1) res--;
}
return res;
}
int main() {
freopen("input","r",stdin);
int c,m;
is>>n>>c>>m;
bsz=(int)sqrt(n);
for(int i=1;i<=n;++i) {
is>>a[i];
bn[i]=(i-1)/bsz+1;
bhe[bn[i]]=bn[i-1]!=bn[i]?i:bhe[bn[i]];
}
bc=bn[n];
for(int i=1;i<=n;++i)
cext[a[i]][bn[i]]++;
for(int i=1;i<=n;++i)
partial_sum(cext[i]+1,cext[i]+1+bc,cext[i]+1);
for(int lb=1;lb<=bc;++lb) {
counter.Reset();
int cnt=0,c;
for(int i=bhe[lb];i<=n;++i) {
if(valid(c=++counter[a[i]])) cnt++;
else if(c!=1) cnt--;
if(bn[i]!=bn[i+1])
bAns[lb][bn[i]]=cnt;
}
}
int Ans=0;
#define decode(x) (((x+=Ans)%=n)+=1)
for(int i=1;i<=m;++i) {
int l,r;is>>l>>r;
decode(l),decode(r);
if(l>r) swap(l,r);
os<<(Ans=Query(l,r))<<'\n';
}
return 0;
}