点此看题面

大致题意: 给你一个\(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维护联通块个数)的更多相关文章

  1. BZOJ1453: [Wc]Dface双面棋盘

    Description Input Output Sample Input Sample Output HINT 线段树套并查集应该是比较好写的做法,时间复杂度为O(N^3+M*NlogN). #in ...

  2. 【BZOJ1453】[Wc]Dface双面棋盘 线段树+并查集

    [BZOJ1453][Wc]Dface双面棋盘 Description Input Output Sample Input Sample Output HINT 题解:话说看到题的第一反应其实是LCT ...

  3. bzoj 1453: [Wc]Dface双面棋盘

    1453: [Wc]Dface双面棋盘 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 617  Solved: 317[Submit][Status][ ...

  4. [Wc]Dface双面棋盘()

    题解: 一道维护奇怪信息的线段树... 我刚开始看了标签想的是删去图上一个点后求连通性 发现不会 于是退化成一般图支持删除 插入 维护连通性 发现有2两种做法 1.lct维护 按照结束顺序先后排序,给 ...

  5. 【刷题】BZOJ 1453 [Wc]Dface双面棋盘

    Description Input Output Sample Input Sample Output HINT Solution 不强制在线的动态图问题,那就LCT了 类似二分图那道题目 对于四个方 ...

  6. BZOJ1453: [WC2005]Dface双面棋盘

    离线LCT维护MST,和3082的方法一样.然而比较码农,适合颓废的时候写. PS:线段树分治要好写得多,LCT比较自娱自乐. #include<bits/stdc++.h> using ...

  7. BZOJ1453:[WC]Dface双面棋盘

    浅谈树状数组与线段树:https://www.cnblogs.com/AKMer/p/9946944.html 题目传送门:https://lydsy.com/JudgeOnline/problem. ...

  8. 利用DFS求联通块个数

    /*572 - Oil Deposits ---DFS求联通块个数:从每个@出发遍历它周围的@.每次访问一个格子就给它一个联通编号,在访问之前,先检查他是否 ---已有编号,从而避免了一个格子重复访问 ...

  9. [BZOJ1453]Dface双面棋盘

    Description Input Output Sample Input Sample Output HINT 线段树+并查集,暴力记录和更新一些信息,详情见代码注解. #include<cm ...

随机推荐

  1. getResourceAsStream小结

    前提:我用的是gradle工程,文件放在resource下,resource对应的就是类路径,文件的路径和代码的路径保持一致,如Client的包名和peizhi.properties一致,例如Clie ...

  2. shiro【filter】

    alt+7 OncePerRequestFilter public final void doFilter(ServletRequest request, ServletResponse respon ...

  3. loadrunner学习--基础知识

    性能测试相关术语: 1.响应时间:指系统从发出请求开始到客户端接受到所有数据所消耗的时间. 2.并发用户:指同一时刻与服务器进行数据交互的所有用户数量.计算公式F=Nvu x R/T 其中F表示吞吐量 ...

  4. redis的三种启动方式,个人常用第二种

    redis的启动方式1.直接启动  进入redis根目录,执行命令:  #加上‘&’号使redis以后台程序方式运行 1 ./redis-server & 2.通过指定配置文件启动  ...

  5. KEIL中头文件使用配置向导

    在xxx.h头文件的代码中夹杂的“<h>      </h>”.“<o>”“<i>”.“<c1>   </c>”和“<e& ...

  6. maya2014安装失败如何卸载重装

    AUTODESK系列软件着实令人头疼,安装失败之后不能完全卸载!!!(比如maya,cad,3dsmax等).有时手动删除注册表重装之后还是会出现各种问题,每个版本的C++Runtime和.NET f ...

  7. LeanTouch控制移动

    Lean_Touch控制移动 using UnityEngine; using System.Collections; using System.Collections.Generic; using ...

  8. Android平台网络常用命令

    工作中经常用到的一些命令,整理一下,方便以后进行参考 1.IP设置 ifconfig eth0 128.224.156.81 up  //一般的嵌入式linux中设置IP.ifconfig eth0 ...

  9. entity framework discriminator error

    前几天使用code first碰到错误:列名 'Discriminator' 无效.这是一个很少见的错误,搜索了很久才发现是code first的poco实体对象的继承问题. 比如,我定义了一个实体类 ...

  10. 案例49-crm练习获取客户列表带有分页和筛选功能

    1 案例分析 2 书写步骤 1.封装PageBean 2.书写Action 3.书写Service 4.书写Dao   注意清空之前设置的聚合函数  dc.setProjection(null); 5 ...