codeforces 811 E. Vladik and Entertaining Flags(线段树+并查集)
题目链接:http://codeforces.com/contest/811/problem/E
题意:给定一个行数为10 列数10w的矩阵,每个方块是一个整数, 给定l和r 求范围内的联通块数量 所谓联通块即数字相等
题解:显然可以用线段树来维护一下,一共就10行。线段树唯一难处理的就是push_up不好弄,这一要利用一下并查集,因为求的是连通块
的个数。具体看一下代码的注释。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int M = 1e5 + 10; struct TnT { int l , r , sum; int lsum[11] , rsum[11]; }T[M << 2]; int f[M << 4] , a[11][M]; int n , m , q , tot; //并查集并的是她们连通块的种类。 void init() { for(int i = 1 ; i <= n * m ; i++) { f[i] = i; } } int find(int x) { if(x == f[x]) return x; int tmp = find(f[x]); return f[x] = tmp; } TnT push_up(int mid , TnT le , TnT re) { TnT ans; ans.sum = le.sum + re.sum; for(int j = 1 ; j <= n ; j++) { f[le.lsum[j]] = le.lsum[j]; f[le.rsum[j]] = le.rsum[j]; f[re.lsum[j]] = re.lsum[j]; f[re.rsum[j]] = re.rsum[j]; }//这里一定要这样赋值一下因为合并的时候这两部分肯定不属于同意连通块,所以不能让她们的父亲相同,而且她们的父亲会在合并的时候变成相同的所以这里要每次给她们定一个新父亲。 for(int j = 1 ; j <= n ; j++) { if(a[j][mid] == a[j][mid + 1]) { int t1 = find(le.rsum[j]) , t2 = find(re.lsum[j]); if(t1 != t2) { ans.sum--; f[t1] = t2; }//显然如果不是相同父亲的sum-- } } for(int j = 1 ; j <= n ; j++) { ans.lsum[j] = find(le.lsum[j]); ans.rsum[j] = find(re.rsum[j]); }//pushup一下ans的lsum于rsum ans.l = le.l , ans.r = re.r; return ans; } void build(int i , int l , int r) { int mid = (l + r) >> 1; T[i].l = l , T[i].r = r , T[i].sum = 0; if(l == r) { for(int j = 1 ; j <= n ; j++) { if(a[j][l] == a[j - 1][l]) { T[i].lsum[j] = T[i].rsum[j] = T[i].lsum[j - 1];//如果相邻两个一样那么她们肯定属于一个连通块所以连通块下表一样, } else { T[i].lsum[j] = T[i].rsum[j] = ++tot; T[i].sum++; } } return ; } build(i << 1 , l , mid); build((i << 1) | 1 , mid + 1 , r); T[i] = push_up(mid , T[i << 1] , T[(i << 1) | 1]); } TnT query(int i , int l , int r) { int mid = (T[i].l + T[i].r) >> 1; if(T[i].l == l && T[i].r == r) { return T[i]; } T[i] = push_up(mid , T[i << 1] , T[(i << 1) | 1]); if(mid < l) return query((i << 1) | 1 , l , r); else if(mid >= r) return query(i << 1 , l , r); else { return push_up(mid , query(i << 1 , l , mid) , query((i << 1) | 1 , mid + 1 , r)); } } int main() { scanf("%d%d%d" , &n , &m , &q); tot = 0; for(int i = 1 ; i <= n ; i++) { for(int j = 1 ; j <= m ; j++) { scanf("%d" , &a[i][j]); } } init(); build(1 , 1 , m); while(q--) { int x , y; scanf("%d%d" , &x , &y); printf("%d\n" , query(1 , x , y).sum); } return 0; }