一、分块
核心就是找到分块要维护的东西。想象一列数字中,我们取出其中相同的一组,我们要在给定范围内取不超过k个,如果暴力从左往右判断然后记录个数,很不现实吧。那我们就开个数组记录这个数在此组中的位置+k个后会不会超过这一组的个数,但之后统计的关键不是“从左往右”。如果碰到的数+k后还在区间内,我们不做记录,因为这样限制条件就会很多,分块询问时中间的块也不好问,很容易出错(但说不定可以写)。我们可以转换思路,专找+k后大于边界的,这说明我们拿上它个数也不会超过k,这样ans++;之后分块,两边一个一个找,中间用二分找到大于边界的,直接加,所以前面要sort预处理。
//写得乱七八糟的题解,可能只有我自己才看得懂~~
1 #include<bits/stdc++.h> 2 #include<iostream> 3 #include<stack> 4 #include<algorithm> 5 #include<cstdio> 6 #include<cmath> 7 #include<cstring> 8 #define mem(a) memset(a,0,sizeof(a)) 9 #define ll long long 10 #define mp make_pair 11 #define pb push_back 12 #define inf 0x3f3f3f3f 13 using namespace std; 14 const int N=1e5+5; 15 int n,blo,num,belong[N],l[N],r[N],d[N],a[N],b[N],mx=0,k; 16 int nx[N]; 17 vector<int>cnt[N]; 18 void build() 19 { 20 blo=sqrt(n); 21 num=n/blo; 22 if(n%blo) num++; 23 for(int i=1;i<=n;i++) 24 { 25 belong[i]=((i-1)/blo)+1; 26 b[i]=nx[i]; 27 } 28 for(int i=1;i<=num;i++) 29 { 30 l[i]=(i-1)*blo+1; 31 r[i]=i*blo; 32 } 33 r[num]=n; 34 for(int i=1;i<=num;i++) 35 sort(b+l[i],b+r[i]+1); 36 } 37 int query(int x,int y) 38 { 39 int ans=0; 40 for(int i=x;i<=min(r[belong[x]],y);i++) 41 if(nx[i]>y) ans++; 42 if(belong[x]!=belong[y]) 43 { 44 for(int i=l[belong[y]];i<=y;i++) 45 if(nx[i]>y) ans++; 46 for(int i=belong[x]+1;i<=belong[y]-1;i++) 47 { 48 int j=b+r[i]+1-upper_bound(b+l[i],b+r[i]+1,y); 49 ans+=j; 50 } 51 } 52 return ans; 53 } 54 int main() 55 { 56 scanf("%d%d",&n,&k); 57 for(int i=1;i<=n;i++) 58 { 59 scanf("%d\n",&a[i]); 60 mx=max(mx,a[i]); 61 } 62 for(int i=1;i<=n;i++) 63 cnt[a[i]].pb(i); 64 for(int i=1;i<N;i++) 65 { 66 for(int j=0;j<cnt[i].size();j++) 67 { 68 if(j+k>=cnt[i].size()) 69 nx[cnt[i][j]]=N; 70 else 71 nx[cnt[i][j]]=cnt[i][j+k]; 72 } 73 } 74 build(); 75 int last=0,m; 76 scanf("%d",&m); 77 while(m--) 78 { 79 int ans=0; 80 int L,R; 81 scanf("%d%d",&L,&R); 82 L=(L+last)%n+1; 83 R=(R+last)%n+1; 84 if(L>R)swap(L,R); 85 if(R-L+1<=k) ans=R-L+1; 86 else ans=query(L,R); 87 printf("%d\n",ans); 88 last=ans; 89 } 90 build(); 91 92 }