【BZOJ1453】[WC] Dface双面棋盘(LCT维护联通块个数)
大致题意: 给你一个\(n*n\)的黑白棋盘,每次将一个格子翻转,分别求黑色连通块和白色连通块的个数。
\(LCT\)动态维护图连通性
关于这一部分内容,可以参考这道例题:【BZOJ4025】二分图。
大致思路
我们可以将同种颜色的相邻格子之间连边,这样就可以把问题搬到图上。
然后考虑先离线,把每两个格子间边的加入与删除的时间(同一条边可能被加入和删除多次)记录下来。
这样就可以用\(LCT\)动态维护图连通性了。
但注意这道题比较恶心,需要你每次求出两种颜色的连通块个数。
我一开始的想法是对于每个询问暴力枚举\(LCT\)的每一个根节点,判断其为黑色还是白色(我们可以给每条边也记录下对应的颜色),从而统计答案,但是毫无悬念地\(TLE\)了。
看来,只能在删边与加边的同时动态维护了。
在加边时,若连接的两个节点在不同的树中,就说明要合并两个连通块,因此将该颜色连通块个数减\(1\)。
在删边时,若连接的两个节点依靠这条边连通,即这条边原先并没有在\(LCT\)中被删掉过,就说明会使一个连通块分成两个,因此将该颜色连通块个数加\(1\)。
要注意的是,当你翻转一个格子时,要将原先颜色的连通块个数减\(1\),并将新颜色的连通块个数加\(1\),不然在加边删边的过程中会出现重复计算的问题。
具体实现可见代码。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200
#define M 10000
#define E ((N*N<<2)+(M<<3))
#define P(x,y) (((x)-1)*n+(y))
#define swap(x,y) (x^=y^=x^=y)
#define mp make_pair
#define INF 1e9
using namespace std;
const int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int n,m,ans[2],a[N*N+5],s[N*N+5],qx[M+5],qy[M+5],tx[N*N+E+5],ty[N*N+E+5],lst[N*N+E+5];map<pair<int,int>,int> t;
struct Operate//存储下每一个操作
{
int x,y,c,t,f,p,d;
I Operate(CI a=0,CI b=0,CI co=0,CI ti=0,CI fl=0):x(a),y(b),c(co),t(ti),f(fl){}
}o[E+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define pc(c) (C^FS?FO[C++]=c:(fwrite(FO,1,C,stdout),FO[(C=0)++]=c))
#define tn(x) (x<<3)+(x<<1)
#define D isdigit(c=tc())
int T,C;char c,*A,*B,FI[FS],FO[FS],S[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn(x)+(c&15),D);}
Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
Tp I void writeln(Con Ty& x) {write(x),pc('\n');}
I void write_space() {pc(' ');}
I void clear() {fwrite(FO,1,C,stdout),C=0;}
}F;
class LinkCutTree//LCT
{
private:
#define GVmin(x,y) (V[O[x].Min]>V[O[y].Min]&&(O[x].Min=O[y].Min))
#define PU(x) (O[x].Min=x,GVmin(x,O[x].S[0]),GVmin(x,O[x].S[1]))//上传子树信息
#define Re(x) (swap(O[x].S[0],O[x].S[1]),O[x].R^=1)
#define PD(x) (O[x].R&&(Re(O[x].S[0]),Re(O[x].S[1]),O[x].R=0))
#define Wh(x) (O[O[x].F].S[1]==x)
#define Co(x,y,d) (O[O[x].F=y].S[d]=x)
#define IR(x) (O[O[x].F].S[0]^x&&O[O[x].F].S[1]^x)
#define MR(x) (Ac(x),S(x),Re(x))
#define Sp(x,y) (MR(x),Ac(y),S(y))
static const int SZ=N*N+E;int St[SZ+5];struct node {int Min,R,F,S[2];}O[SZ+5];
I void Ro(CI x) {RI f=O[x].F,p=O[f].F,d=Wh(x);!IR(f)&&(O[p].S[Wh(f)]=x),O[x].F=p,Co(O[x].S[d^1],f,d),Co(f,x,d^1),PU(f),PU(x);}
I void S(CI x) {RI f=x,T=0;W(St[++T]=f,!IR(f)) f=O[f].F;W(T) PD(St[T]),--T;W(!IR(x)) f=O[x].F,!IR(f)&&(Ro(Wh(x)^Wh(f)?x:f),0),Ro(x);}
I void Ac(RI x) {for(RI s=0;x;x=O[s=x].F) S(x),O[x].S[1]=s,PU(x);}
public:
int C[SZ+5],V[SZ+5],T[SZ+5];I LinkCutTree() {V[0]=INF+1;}
I void Init(CI n) {for(RI i=1,s=n*n;i<=s;++i) V[O[i].Min=i]=INF+1,C[i]=a[i],T[i]=1;}//初始化每个点被删除时间为INF+1,记录其颜色,标记其在树中
I void Link(CI x,CI y) {MR(x),O[x].F=y;}I void Cut(CI x,CI y) {MR(x),Ac(y),S(x),O[y].F=O[x].S[1]=0,PU(x);}
I int FR(RI x) {Ac(x),S(x);W(O[x].S[0]) PD(x),x=O[x].S[0];return S(x),x;}
I int QMin(CI x,CI y) {return Sp(x,y),O[y].Min;}//查询子树中最早被删掉的边
I int Query(CI tot,CI op) {RI i,res=0;for(i=1;i<=tot;++i) T[i]&&!O[i].F&&!(C[i]^op)&&++res;return res;}//询问颜色为op的连通块个数(用于初始化ans)
I void RC(CI x) {C[x]^=1;}//翻转某个点的颜色
}T;
I void Add(CI pos)//加入一条边
{
RI x=o[pos].x,y=o[pos].y,z=o[pos].p;
if(T.C[z]=o[pos].c,T.V[z]=o[pos].d,T.FR(x)^T.FR(y)) return --ans[T.C[z]],T.T[z]=1,T.Link(x,z),T.Link(z,y);//合并两个连通块,则将该颜色连通块个数减1
RI p=T.QMin(x,y);if(T.V[z]<T.V[p]) return;
T.T[p]=0,T.Cut(tx[p],p),T.Cut(p,ty[p]),T.T[z]=1,T.Link(x,z),T.Link(z,y);
}
I void Del(CI pos)//删除一条边
{
RI x=o[pos].x,y=o[pos].y,z=o[pos].p;
!(T.FR(x)^T.FR(z))&&!(T.FR(y)^T.FR(z))&&(++ans[T.C[z]],T.T[z]=0,T.Cut(x,z),T.Cut(z,y),0);//分裂一个连通块,则将该颜色连通块个数加1
}
int main()
{
RI i,j,k=1,tot,cnt=0,p,nx,ny,p1,p2;Reg pair<int,int> w;
for(F.read(n),i=1;i<=n;++i) for(j=1;j<=n;++j) F.read(a[P(i,j)]),s[P(i,j)]=a[P(i,j)];//读入,用s数组复制一遍a数组
for(T.Init(n),i=1;i<=n;++i) for(j=1;j<=n;++j)//初始化出原图的边
{
i^n&&!(a[P(i,j)]^a[P(i+1,j)])&&(o[++cnt]=Operate(P(i,j),P(i+1,j),a[P(i,j)],0,1),0),//向下
j^n&&!(a[P(i,j)]^a[P(i,j+1)])&&(o[++cnt]=Operate(P(i,j),P(i,j+1),a[P(i,j)],0,1),0);//向右
}
for(F.read(m),i=1;i<=m;++i)//离线处理出每个加边和删边操作
{
for(F.read(qx[i],qy[i]),a[p=P(qx[i],qy[i])]^=1,j=0;j^4;++j)
{
if((nx=qx[i]+dx[j])<1||nx>n||(ny=qy[i]+dy[j])<1||ny>n) continue;
p1=p,p2=P(nx,ny),p1>p2&&swap(p1,p2),o[++cnt]=Operate(p1,p2,a[p1]^a[p2]?-1:a[p1],i,a[p1]^a[p2]?-1:1);
}
}
for(tot=n*n,i=1;i<=cnt;++i) !t[w=mp(o[i].x,o[i].y)]&&(t[w]=++tot,tx[t[w]]=o[i].x,ty[t[w]]=o[i].y),o[i].p=t[w];//给边标号,这样便于将边看作一个节点
for(i=cnt;i;--i) ~o[i].f?(o[i].d=lst[o[i].p]?lst[o[i].p]:INF):(lst[o[i].p]=o[i].t);//倒序枚举,求出每条边的出现和删除时间
W(k<=cnt&&!o[k].t) Add(k++);//先将原图的边加到图上
for(ans[0]=T.Query(tot,0),ans[1]=T.Query(tot,1),i=1;i<=m;++i)//初始化ans,然后处理操作
{
p=P(qx[i],qy[i]),--ans[s[p]],++ans[s[p]^=1],T.RC(p);W(k<=cnt&&!(o[k].t^i)) ~o[k].f?Add(k++):Del(k++);//更新信息
F.write(ans[1]),F.write_space(),F.writeln(ans[0]);//输出答案
}
return F.clear(),0;
}
【BZOJ1453】[WC] Dface双面棋盘(LCT维护联通块个数)的更多相关文章
- BZOJ1453: [Wc]Dface双面棋盘
Description Input Output Sample Input Sample Output HINT 线段树套并查集应该是比较好写的做法,时间复杂度为O(N^3+M*NlogN). #in ...
- 【BZOJ1453】[Wc]Dface双面棋盘 线段树+并查集
[BZOJ1453][Wc]Dface双面棋盘 Description Input Output Sample Input Sample Output HINT 题解:话说看到题的第一反应其实是LCT ...
- bzoj 1453: [Wc]Dface双面棋盘
1453: [Wc]Dface双面棋盘 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 617 Solved: 317[Submit][Status][ ...
- [Wc]Dface双面棋盘()
题解: 一道维护奇怪信息的线段树... 我刚开始看了标签想的是删去图上一个点后求连通性 发现不会 于是退化成一般图支持删除 插入 维护连通性 发现有2两种做法 1.lct维护 按照结束顺序先后排序,给 ...
- 【刷题】BZOJ 1453 [Wc]Dface双面棋盘
Description Input Output Sample Input Sample Output HINT Solution 不强制在线的动态图问题,那就LCT了 类似二分图那道题目 对于四个方 ...
- BZOJ1453: [WC2005]Dface双面棋盘
离线LCT维护MST,和3082的方法一样.然而比较码农,适合颓废的时候写. PS:线段树分治要好写得多,LCT比较自娱自乐. #include<bits/stdc++.h> using ...
- BZOJ1453:[WC]Dface双面棋盘
浅谈树状数组与线段树:https://www.cnblogs.com/AKMer/p/9946944.html 题目传送门:https://lydsy.com/JudgeOnline/problem. ...
- 利用DFS求联通块个数
/*572 - Oil Deposits ---DFS求联通块个数:从每个@出发遍历它周围的@.每次访问一个格子就给它一个联通编号,在访问之前,先检查他是否 ---已有编号,从而避免了一个格子重复访问 ...
- [BZOJ1453]Dface双面棋盘
Description Input Output Sample Input Sample Output HINT 线段树+并查集,暴力记录和更新一些信息,详情见代码注解. #include<cm ...
随机推荐
- 练习五十六:for循环
某个公司采用公用电话传递数据,数据是四位的整数,在传递过程中是加密的,加密规则如下:每位数字都加上5,然后用和除以10的余数代替该数字,再将第一位和第四位交换,第二位和第三位交换 方法一: def o ...
- n阶乘,位数,log函数,斯特林公式
一.log函数 头文件: #include <math.h> 使用: 引入#include<cmath> 以e为底:log(exp(n)) 以10为底:log10(n) 以m为 ...
- sf03_杨辉三角go实现
package main import "fmt" /* 变量规范 全局变量以v_为前缀 函数形参以p_为前缀 函数内部变量,字母数字下划线等普通组合,其中函数返回值以out_为前 ...
- 关于element-ui表格样式设置的方法cell-class-name
关于element-ui表格使用的一些方法 最近在用Vue.js和elment-ui做一个后台管理项目,不得不说element功能非常强大,提供了许多组件,基本可以满足一些基础的开发了.因为我做的后台 ...
- 查看linux系统各种参数配置的命令
查看linux系统各种参数配置的命令 last |grep shutdown //查看上次关机时间 last |grep reboot ...
- 前端面试题 ---- html篇
想要换工作了,转载自https://www.cnblogs.com/zhangshuda/p/8464772.html,感谢原博主. 一.html 1.html和xhtml区别 1. html:超文本 ...
- getElementsByTagName() 兼容性
写东西的时候用到了getElementsByTagName(),我一直以为js 写的东西是不会有兼容性的,这次在ie8下,getElementsByTagName()就不支持了,那怎么办呢,我就查到一 ...
- python3.7安装Scrapy
环境:windows 7. 安装过程中遇到的问题 1.error: Unable to find vcvarsall.bat 2.1083: Cannot open include file: 'ba ...
- Murano Weekly Meeting 2015.10.20
Meeting time: 2015.October.20th 1:00~2:00 Chairperson: Serg Melikyan, PTL from Mirantis Meeting sum ...
- Murano Weekly Meeting 2015.09.15
Meeting time: 2015.September.15th 1:00~2:00 Chairperson: Serg Melikyan, PTL from Mirantis Meeting s ...