洛谷P4135 作诗 --分块基础
作为我分块入门的第一道题
这道题花了我一整天的时间才搞出来
整理一下分块很实用思路:
分块的主要思路:比如操作区间[l,r]
1.如果l,r在一个分块里或者相邻的分块里,直接暴力
2. 否则,边角块暴力+中间分块整体解决
总体时间复杂度是n√n 能承受的数据范围是100000以内, 可以说很大了
想思路的难点就在于如何预处理,讲几个常用的预处理办法:
1. F[i][j] 数组存从[i,j]的答案
2. G[i][j] 数组存的j 在块 i 里面的特征, 可以用前缀和表示
有些预处理比较有难度,比如这一题的F[i][j],要点是:不要重复劳动,不要重复走一个区域
一遍就把所有该求的东西求出来
讲几个点吧:
1. 能不用Map就不用Map ,map虽然是stl里面的很方便,但是使用一次就是logn的时间,对时间要求紧的题目就不要用了
2. 分块的几个公式不要写错
1 int n, S, N; 2 int A[maxn], ID[maxn]; 3 4 S = sqrt(n);//初始化 5 N = (n-1)/S + 1; 6 for(int i = 1;i <= n;i++) { 7 scanf("%d",&A[i]); 8 ID[i] = (i-1)/S + 1; 9 }
3.O(1)初始化的技巧要会,用一个次数t来代表第几次使用cnt数组,然后把次数更新在cntM数组里面
1 if(cntM[A[i]] != t) { 2 cntM[A[i]] = t; 3 cnt[A[i]] = 0; 4 }
4.如果可以的话,快速读入也是很重要的
5. 在处理边角块的时候, for循环的使用技巧也很重要,比如跨区域扫描。
1 for(int i = x;i <= y;) { 2 3 if(i == ID[x]*S) i = (ID[y]-1)*S+1; 4 else i++; 5 }
6. 还有交换x,y变量的骚技巧:
1 x^=y^=x^=y;
完整代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<map> 4 #include<cmath> 5 #include<iostream> 6 using namespace std; 7 const int maxn = 100010; 8 const int maxm = 333; 9 10 int n, m, c, S, N; 11 int A[maxn], ID[maxn], cnt[maxn], Q[maxn], cntM[maxn]; 12 int F[maxm][maxm], G[maxm][maxn]; 13 14 inline int query(int x,int y,int t) { 15 int ret = 0; 16 17 if(ID[x] + 1 >= ID[y] ) { 18 for(int i = x;i <= y;i++) { 19 if(cntM[A[i]] != t) { 20 cntM[A[i]] = t; 21 cnt[A[i]] = 0; 22 } 23 cnt[A[i]]++; 24 if(cnt[A[i]]%2 == 0 && cnt[A[i]] >= 2) ret++; 25 else if(cnt[A[i]]%2 == 1 && cnt[A[i]] >= 2) ret--; 26 } 27 return ret; 28 } 29 30 ret = F[ID[x]+1][ID[y]-1]; 31 32 33 for(int i = x;i <= y;) { 34 if(cntM[A[i]] != t) { 35 cntM[A[i]] = t; 36 cnt[A[i]] = 0; 37 } 38 cnt[A[i]]++; 39 40 int tp = G[ID[y]-1][A[i]] - G[ID[x]][A[i]]; 41 int sum = tp + cnt[A[i]]; 42 43 if(sum <= 1) { 44 if(i == ID[x]*S) i = (ID[y]-1)*S+1; 45 else i++; 46 continue; 47 } 48 49 if(sum%2 == 1) ret--; 50 if(sum%2 == 0) ret++; 51 52 if(i == ID[x]*S) i = (ID[y]-1)*S+1; 53 else i++; 54 } 55 return ret; 56 } 57 58 int main() { 59 //freopen("d.txt","r",stdin); 60 scanf("%d %d %d",&n,&c,&m); 61 S = sqrt(n); 62 N = (n-1)/S + 1; 63 for(int i = 1;i <= n;i++) { 64 scanf("%d",&A[i]); 65 ID[i] = (i-1)/S + 1; 66 } 67 68 int sum = 0; 69 for(int i = 1;i <= N;i++) { 70 sum = 0; 71 int t = i; 72 for(int j = (i-1)*S + 1;j <= n;j++) { 73 int p = ID[j]; 74 if(cntM[A[j]] != t) { 75 cntM[A[j]] = t; 76 cnt[A[j]] = 0; 77 } 78 cnt[A[j]]++; 79 if(cnt[A[j]]%2 == 0 && cnt[A[j]] >= 2) sum++; 80 else if(cnt[A[j]]%2 == 1 && cnt[A[j]] >= 2) sum--; 81 if(j%S == 0 || j == n) F[i][p] = sum; 82 } 83 } 84 85 memset(cnt,0,sizeof(cnt)); 86 for(int i = 1;i <= n;i++) { 87 cnt[A[i]]++; 88 if(i%S == 0 || i == n) { 89 for(int j = 1;j <= c;j++) G[ID[i]][j] = cnt[j]; 90 } 91 } 92 93 int ans = 0; 94 for(int i = 1,x,y;i <= m;i++) { 95 scanf("%d %d",&x,&y); 96 x=(x+ans)%n+1; 97 y=(y+ans)%n+1; 98 if(x>y) x^=y^=x^=y; 99 // cout<<x<<" : "<<y<<endl; 100 ans = query(x,y,i+N); 101 printf("%d\n",ans); 102 } 103 104 return 0; 105 }