題意
一個的地圖,一開始全是顏色0.
Q次詢問,每次把的格子變成顏色。問每次操作完之後有多少個同色聯通塊。
保證以單調不降的順序給出,且保證
解題思路
可以按照不同的顏色分別統計。那麼修改一個位置的顏色相當於在一種顏色的集合刪掉一個位置,在另一個顏色的集合種加入一個位置。
因爲保證是不降的,所以刪除操作之後不可能出現加入操作。因此問題就轉換成了對於一個圖,求刪掉一些邊之後連通塊個數。這是並查集的經典問題:刪邊並查集。可以把順序的刪邊,想成逆序的加邊。
因爲刪邊並查集需要把沒有刪過的邊加入,所以我們在最後存留的圖裏面把那些對應的顏色再加入到刪除序列中,然後讓他們不參與答案的統計,這樣在統計答案的時候這些沒有刪的位置就已經在圖中了。
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define fors(i, a, b) for(int i = (a); i < (b); ++i)
#define P pair<int,int>
using namespace std;
int n, m, q;
const int maxn = 2e5 + 50;
int rk[maxn], fa[maxn];
int fnd(int x){return (x == fa[x])?x:fa[x] = fnd(fa[x]);}
int link(int u, int v){
u = fnd(u); v = fnd(v);
if(u == v) return 0;
if(rk[u] < rk[v]) swap(u, v);
fa[v] = u; if(rk[u] == rk[v]) rk[u]++;
return 1;
}
vector<int> g[maxn];
int a[maxn];
void init(){
scanf("%d%d%d", &n, &m, &q);
fors(i, 0, n*m) fa[i] = i;
fors(i, 0, n) fors(j, 0, m-1) {
int u = i*m+j, v = i*m+j+1;
g[u].pb(v); g[v].pb(u);
}
fors(j, 0, m) fors(i, 0, n-1){
int u = i*m+j, v = (i+1)*m+j;
g[u].pb(v); g[v].pb(u);
}
}
vector<P> add[maxn*10], del[maxn*10];
int d[maxn*10];
void work(vector<P> &c, int op){
fors(i, 0, n) fors(j, 0, m) a[i*m+j] = 0, fa[i*m+j] = i*m+j, rk[i*m+j] = 1;
for(auto t: c){
int u = t.first, id = t.second;
int cur = 1; a[u] = 1;
for(int v: g[u]){
if(a[v]) cur -= link(u, v);
}
d[id] += cur*op;
}
}
void sol(){
int mx = 1;
for(int i = 0; i < q; ++i){
int x, y, c; scanf("%d%d%d", &x, &y, &c);x--;y--;
int u = x*m+y;
if(c == a[u]) continue;
mx = c+1;
add[c].pb(P(u, i));
del[a[u]].pb(P(u, i));
a[u] = c;
}
fors(i, 0, n*m) del[a[i]].pb(P(i, q));
fors(i, 0, mx) reverse(del[i].begin(), del[i].end());
fors(i, 0, mx) work(add[i], 1);
fors(i, 0, mx) work(del[i], -1);
int ans = 1;
fors(i, 0, q) ans += d[i], printf("%d\n", ans);
}
int main()
{
init();
sol();
}