洛谷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 } 

 

posted @ 2018-08-21 15:58  Frank的成长之路  阅读(187)  评论(0编辑  收藏  举报