P4121 [WC2005] 双面棋盘 题解
2023-07-26 14:58:54 solution
简化题意:
给你一个 \(n\times n\) 的只有黑白两种颜色的棋盘,每次修改把某格的黑白互换,求每次修改后黑白各自的连通块个数。
解法
将棋盘转化成 \(n\) 行,每行单独处理,处理出黑色和白色分别的连通块个数(可用并查集)。
开一个 \(n\) 的线段树,每个叶子节点表示一行。
合并过程中,先将黑白的连通块数量相加作为合并后的连通块数量,再用并查集将颜色相同但是非同一个指向的并查集合并并减去一个连通块个数。
每次修改都重新修改对应行的并查集数组。
修改行和合并行复杂度都是 \(O(n\log n)\)。
单次修改复杂度 \(O(n\log^2n)\)。
总复杂度 \(O(nm\log ^2 n)\),\(n\leq 200\),\(m \leq 10000\),可过。
Code
int n,m;
int fa[40004];
inline int gl(int x,int y){
return n*(x-1)+y;
}
inline int find(int x){
if(x==fa[x])return x;
return fa[x]=find(fa[x]);
}
struct line{
int up[205],down[205],bl,wh,hup,hdown;//h 针对单行位置
int ls[205],rs[205];
void recreate(){ //单行暴力修改
bl=wh=0;
for(int i=1;i<=n;i++){
ls[i]=rs[i]=fa[gl(hup,i)]=gl(hup,i);
bl+=up[i];
wh+=up[i]^1;
}
for(int i=2;i<=n;i++){
if(up[i]==up[i-1]){
ls[i]=rs[i]=fa[find(gl(hup,i))]=find(gl(hup,i-1));
bl-=up[i];
wh-=up[i]^1;
}
}
}
}tr[806];
inline void pushup(int root){
tr[root].bl=tr[root<<1].bl+tr[root<<1|1].bl;
tr[root].wh=tr[root<<1].wh+tr[root<<1|1].wh;
for(int i=1;i<=n;i++){
tr[root].ls[i]=tr[root<<1].ls[i];
tr[root].rs[i]=tr[root<<1|1].rs[i];
fa[tr[root<<1].ls[i]]=tr[root<<1].ls[i];
fa[tr[root<<1].rs[i]]=tr[root<<1].rs[i];
fa[tr[root<<1|1].ls[i]]=tr[root<<1|1].ls[i];
fa[tr[root<<1|1].rs[i]]=tr[root<<1|1].rs[i];
tr[root].up[i]=tr[root<<1].up[i];
tr[root].down[i]=tr[root<<1|1].down[i];
}
tr[root].hup=tr[root<<1].hup;
tr[root].hdown=tr[root<<1|1].hdown;
for(int i=1;i<=n;i++){
int upfa=find(tr[root<<1].rs[i]),downfa=find(tr[root<<1|1].ls[i]);
if(tr[root<<1].down[i]==tr[root<<1|1].up[i]&&upfa!=downfa){
fa[upfa]=downfa;
tr[root].bl-=tr[root<<1].down[i];
tr[root].wh-=tr[root<<1].down[i]^1;
}
}
for(int i=1;i<=n;i++){
tr[root].ls[i]=find(tr[root].ls[i]),tr[root].rs[i]=find(tr[root].rs[i]);
}
}
void build(int root,int l,int r){
if(l==r){
for(int i=1;i<=n;i++){
tr[root].up[i]=tr[root].down[i]=read();
}
tr[root].hup=tr[root].hdown=l;
tr[root].recreate();
//printf("line %d: bl:%d wh:%d\n",l,tr[root].bl,tr[root].wh);
return;
}
build(root<<1,l,mid),build(root<<1|1,mid+1,r);
pushup(root);
//printf("line %d-%d: bl:%d wh:%d\n",l,r,tr[root].bl,tr[root].wh);
}
void update(int root,int l,int r,int x,int y){
if(l==r){
tr[root].up[y]^=1;
tr[root].down[y]^=1;
tr[root].recreate();
//printf("line %d: bl:%d wh:%d\n",l,tr[root].bl,tr[root].wh);
return;
}
if(mid>=x)update(root<<1,l,mid,x,y);
else update(root<<1|1,mid+1,r,x,y);
pushup(root);
//printf("line %d-%d: bl:%d wh:%d\n",l,r,tr[root].bl,tr[root].wh);
}
int main(){
n=read();
build(1,1,n);
m=read();
while(m--){
int x=read(),y=read();
update(1,1,n,x,y);
printf("%d %d\n",tr[1].bl,tr[1].wh);
}
}

浙公网安备 33010602011771号