标签:分块 ring 文件 continue tin 方法 位置 表示 编程
题目描述
输入
输出
样例输入
2 3
1 2
Q 1 2
R 1 2
Q 1 2
样例输出
2
1
题解
分块+二分
两道一样的题。。。
对于每个i记录一个p[i],代表i前一个颜色与i相同的位置,没有则为-1。
那么对于区间[l,r]中的i,如果p[i]<l,那么i就是[l,r]中第一个出现c[i]颜色的,可以记录到答案中。
然后分成√n 个块,建立新数组对块中p[i]排序。
查询时,整块使用排序后的数组二分查找小于l的个数,多余部分暴力查询。
修改时,需要考虑3部分:i后面第一个c[j]==c[x],i后面第一个c[j]==y,i前面第一个c[j]==y。
于是我们还需要建立新数组对块中c[i]排序,并使用二分查找来完成这三个操作。
具体方法:x块内暴力查找,x块外二分查找。
这里压了大量的行,并使用flag减小花括号,不然太多行实在要看吐了QAQ。
还是别忘特判,另外注意i的含义,位置号或块号。
看到网上还有暴力修改的方法,不过亲测很慢,不推荐。
#include <cstdio> #include <cmath> #include <cstring> #include <algorithm> using namespace std; int n , si , c[10010] , vc[10010] , p[10010] , vp[10010] , last[1000010]; char str[5]; void reset(int b) { int l = b * si , r = min((b + 1) * si - 1 , n - 1) , i; for(i = l ; i <= r ; i ++ ) vc[i] = c[i] , vp[i] = p[i]; sort(vc + l , vc + r + 1) , sort(vp + l , vp + r + 1); } int query(int b , int lim) { int l = b * si , r = min((b + 1) * si - 1 , n - 1) , mid , ans = l - 1 , tl = l; while(l <= r) { mid = (l + r) >> 1; if(vp[mid] < lim) ans = mid , l = mid + 1; else r = mid - 1; } return ans - tl + 1; } bool findc(int l , int r , int x) { int mid; while(l <= r) { mid = (l + r) >> 1; if(vc[mid] == x) return 1; else if(vc[mid] < x) l = mid + 1; else r = mid - 1; } return 0; } int main() { int m , i , j , x , y , ans , flag , l , r; scanf("%d%d" , &n , &m); si = (int)sqrt(n); memset(last , -1 , sizeof(last)); for(i = 0 ; i < n ; i ++ ) scanf("%d" , &c[i]) , p[i] = last[c[i]] , last[c[i]] = i; for(i = 0 ; i * si < n ; i ++ ) reset(i); while(m -- ) { scanf("%s%d%d" , str , &x , &y); x -- ; if(str[0] == ‘Q‘) { y -- ; ans = 0; if(x / si == y / si) for(i = x ; i <= y ; i ++ ) ans += (p[i] < x); else { for(i = x / si + 1 ; i < y / si ; i ++ ) ans += query(i , x); for(i = x ; i < (x / si + 1) * si ; i ++ ) ans += (p[i] < x); for(i = y / si * si ; i <= y ; i ++ ) ans += (p[i] < x); } printf("%d\n" , ans); } else { if(c[x] == y) continue; flag = 0; for(i = x + 1 ; !flag && i < (x / si + 1) * si && i < n ; i ++ ) if(p[i] == x) p[i] = p[x] , reset(i / si) , flag = 1; for(i = x / si + 1 ; !flag && i * si < n ; i ++ ) { l = i * si , r = min((i + 1) * si - 1 , n - 1); if(findc(l , r , c[x])) for(j = l ; !flag && j <= r ; j ++ ) if(c[j] == c[x]) p[j] = p[x] , reset(i) , flag = 1; } c[x] = y; flag = 0; for(i = x + 1 ; !flag && i < (x / si + 1) * si && i < n ; i ++ ) if(c[i] == c[x]) p[i] = x , reset(i / si) , flag = 1; for(i = x / si + 1 ; !flag && i * si < n ; i ++ ) { l = i * si , r = min((i + 1) * si - 1 , n - 1); if(findc(l , r , c[x])) for(j = l ; !flag && j <= r ; j ++ ) if(c[j] == c[x]) p[j] = x , reset(i) , flag = 1; } flag = 0; for(i = x - 1 ; !flag && i >= x / si * si ; i -- ) if(c[i] == c[x]) p[x] = i , flag = 1; for(i = x / si - 1 ; !flag && ~i ; i -- ) { l = i * si , r = min((i + 1) * si - 1 , n - 1); if(findc(l , r , c[x])) for(j = r ; !flag && j >= l ; j -- ) if(c[j] == c[x]) p[x] = j , flag = 1; } if(!flag) p[x] = -1; reset(x / si); } } }
【bzoj2453】维护队列/【bzoj2120】数颜色 分块+二分
标签:分块 ring 文件 continue tin 方法 位置 表示 编程
原文地址:http://www.cnblogs.com/GXZlegend/p/6591383.html