Description
Input
Output
Sample Input
![](https://www.lydsy.com/JudgeOnline/images/1453_4.jpg)
Sample Output
![](https://www.lydsy.com/JudgeOnline/images/1453_5.jpg)
HINT
题解Here!
用线段树维护每行的同色块,每个节点套并查集。
记录最上方和最下方的联通性,暴力合并+暴力修改并查集,并记录答案。
并查集的合并比较恶心。。。
用并查集中的$[1,n]$表示上方,$[n+1,2n]$表示下方。
合并时$[2n+1,4n]$用于右侧的上下方并查集。
话说我的线段树常数炒鸡大,卡了好久才卡过去的。。。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<cstring>
#define LSON rt<<1
#define RSON rt<<1|1
#define DATA(x) a[x].data
#define WHITE(x) a[x].white
#define BLACK(x) a[x].black
#define LSIDE(x) a[x].l
#define RSIDE(x) a[x].r
#define MAXN 210
using namespace std;
int n,m;
int chess[MAXN][MAXN],map[MAXN<<2];
struct Set{
int father[MAXN<<2];
inline void init(){for(int i=0;i<=(n<<2);i++)father[i]=i;}
int find(int x){return father[x]==x?x:father[x]=find(father[x]);}
inline void uniun(int x,int y){father[find(x)]=find(y);}
inline bool check(int x,int y){return (find(x)==find(y));}
};
struct Segment_Tree{
Set data;
int white,black,l,r;
}a[MAXN<<2];
inline int read(){
int date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
void pushup(int rt){
int mid=LSIDE(rt)+RSIDE(rt)>>1;
WHITE(rt)=WHITE(LSON)+WHITE(RSON);
BLACK(rt)=BLACK(LSON)+BLACK(RSON);
DATA(rt).init();
for(int i=1;i<=(n<<1);i++){
DATA(rt).uniun(i,DATA(LSON).find(i));
DATA(rt).uniun(i+(n<<1),DATA(RSON).find(i)+(n<<1));
}
for(int i=1;i<=n;i++)
if(chess[mid][i]==chess[mid+1][i]){
if(DATA(rt).check(i+n,i+(n<<1)))continue;
DATA(rt).uniun(i+n,i+(n<<1));
WHITE(rt)-=chess[mid][i]^1;
BLACK(rt)-=chess[mid][i];
}
for(int i=1;i<=(n<<2);i++){
DATA(rt).find(i);
map[i]=0;
}
for(int i=1;i<=n;i++){
if(!map[DATA(rt).father[i]]){
map[DATA(rt).father[i]]=i;
DATA(rt).father[i]=i;
}
else DATA(rt).father[i]=map[DATA(rt).father[i]];
}
for(int i=n*3+1;i<=(n<<2);i++){
if(!map[DATA(rt).father[i]]){
map[DATA(rt).father[i]]=i-(n<<1);
DATA(rt).father[i]=i-(n<<1);
}
else DATA(rt).father[i]=map[DATA(rt).father[i]];
}
for(int i=1;i<=n;i++)DATA(rt).father[i+n]=DATA(rt).father[i+n*3];
for(int i=(n<<1)+1;i<=(n<<2);i++)DATA(rt).father[i]=i;
}
void buildtree(int l,int r,int rt){
LSIDE(rt)=l;RSIDE(rt)=r;
if(l==r){
WHITE(rt)=chess[l][1]^1;
BLACK(rt)=chess[l][1];
DATA(rt).init();
DATA(rt).uniun(1+n,1);
for(int i=2;i<=n;i++){
DATA(rt).uniun(i+n,i);
if(chess[l][i-1]==chess[l][i])DATA(rt).uniun(i,i-1);
else{
WHITE(rt)+=chess[l][i]^1;
BLACK(rt)+=chess[l][i];
}
}
return;
}
int mid=l+r>>1;
buildtree(l,mid,LSON);
buildtree(mid+1,r,RSON);
pushup(rt);
}
void update(int k,int rt){
if(LSIDE(rt)==RSIDE(rt)){
WHITE(rt)=chess[k][1]^1;
BLACK(rt)=chess[k][1];
DATA(rt).init();
DATA(rt).uniun(1+n,1);
for(int i=2;i<=n;i++){
DATA(rt).uniun(i+n,i);
if(chess[k][i-1]==chess[k][i])DATA(rt).uniun(i,i-1);
else{
WHITE(rt)+=chess[k][i]^1;
BLACK(rt)+=chess[k][i];
}
}
return;
}
int mid=LSIDE(rt)+RSIDE(rt)>>1;
if(k<=mid)update(k,LSON);
else update(k,RSON);
pushup(rt);
}
int main(){
int x,y;
n=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
chess[i][j]=read();
buildtree(1,n,1);
m=read();
while(m--){
x=read();y=read();
chess[x][y]^=1;
update(x,1);
printf("%d %d\n",BLACK(1),WHITE(1));
}
return 0;
}
然后当然是毒瘤的离线$LCT$辣!
我们可以将每个格子看做一个点, 与旁边格子有四条边相连。
处理的过程中显然会遇到环的情况, 怎么处理呢?
同样, 我们维护有关删除时间的最大生成树, 在加入边的时候判断是否为环。
如果成为环, 则与原路径中删除时间最早的边比较, 若新加入边的删除时间更晚则删除之前的那条边。
至于更新色块的个数, 我们考虑格子四个方向的连边:
如果原来颜色相同则删边, 颜色不同则连边。
因为我们保证没有环且保证路径上点删除时间尽量大, 所以我们可以用LCT维护联通信息, 只需判断是否在一棵树内即可。
不过此题离线实在恶心。。。
想想我们需要离线什么?
首先, 我们要预处理一开始边的连通性及初始答案。
其次, 我们要离线所有的修改操作, 分为四个方向考虑。
然后我们还要处理每条边删除的时间, 并以此为权值在$LCT$中连边。
注意还会有多次加入的情况。。。
然后就是一大堆细节问题了。。。
本蒟蒻已经将毁天灭地的所有调试代码删去了,可放心食用。
$namespace$封装看不习惯不要打我。。。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define MAXN 40010
#define MAXM 210
#define MAX 2147483646
using namespace std;
const int fx[4]={1,-1,0,0},fy[4]={0,0,1,-1};
int n,m;
int ans[2],chess[MAXM][MAXM],backup[MAXM][MAXM];
struct Question{
int x,y;
}que[MAXN];
inline int read(){
int date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
namespace MAP{
int num=0;
int cut[MAXN<<3];
struct Edge{
int x,y,start,end,id,colour;
bool flag;
friend bool operator <(const Edge &p,const Edge &q){
if(p.start==q.start)return p.flag<q.flag;
return p.start<q.start;
}
}a[MAXN<<3];
inline int point_id(int x,int y){return (x-1)*n+y;}
inline int edge_id(int x1,int y1,int x2,int y2){
if(x1==x2){
if(y1>y2)swap(y1,y2);
return (x1-1)*(n-1)+y1;
}
else{
if(x1>x2)swap(x1,x2);
return n*(n-1)+(x1-1)*n+y1;
}
}
inline void add_edge(int x,int y,int start,int id,int colour,bool flag){
num++;
a[num].x=x;a[num].y=y;a[num].start=start;a[num].id=id;a[num].colour=colour;
a[num].flag=flag;
}
inline bool check(int x,int y){
if(x<1||x>n||y<1||y>n)return true;
return false;
}
inline void build(){
sort(a+1,a+num+1);
memset(cut,-1,sizeof(cut));
}
}
namespace LCT{
int top=0,stack[MAXN<<4],left[MAXN<<3],right[MAXN<<3];
struct Link_Cut_Tree{
int f,flag,son[2];
int val,minn,pos;
bool cut;
}a[MAXN<<4];
inline bool isroot(int rt){
return a[a[rt].f].son[0]!=rt&&a[a[rt].f].son[1]!=rt;
}
inline void clean(int rt){
a[rt].son[0]=a[rt].son[1]=a[rt].f=a[rt].flag=a[rt].pos=a[rt].cut=0;
a[rt].val=a[rt].minn=MAX;
}
inline void pushup(int rt){
if(!rt)return;
a[rt].minn=a[rt].val;a[rt].pos=rt;
if(a[rt].son[0]&&a[a[rt].son[0]].minn<a[rt].minn){a[rt].minn=a[a[rt].son[0]].minn;a[rt].pos=a[a[rt].son[0]].pos;}
if(a[rt].son[1]&&a[a[rt].son[1]].minn<a[rt].minn){a[rt].minn=a[a[rt].son[1]].minn;a[rt].pos=a[a[rt].son[1]].pos;}
}
inline void pushdown(int rt){
if(!rt||!a[rt].flag)return;
a[a[rt].son[0]].flag^=1;a[a[rt].son[1]].flag^=1;a[rt].flag^=1;
swap(a[rt].son[0],a[rt].son[1]);
}
inline void turn(int rt){
int x=a[rt].f,y=a[x].f,k=a[x].son[0]==rt?1:0;
if(!isroot(x)){
if(a[y].son[0]==x)a[y].son[0]=rt;
else a[y].son[1]=rt;
}
a[rt].f=y;a[x].f=rt;a[a[rt].son[k]].f=x;
a[x].son[k^1]=a[rt].son[k];a[rt].son[k]=x;
pushup(x);pushup(rt);
}
void splay(int rt){
top=0;
stack[++top]=rt;
for(int i=rt;!isroot(i);i=a[i].f)stack[++top]=a[i].f;
while(top)pushdown(stack[top--]);
while(!isroot(rt)){
int x=a[rt].f,y=a[x].f;
if(!isroot(x)){
if((a[y].son[0]==x)^(a[x].son[0]==rt))turn(rt);
else turn(x);
}
turn(rt);
}
}
inline void access(int rt){
for(int i=0;rt;i=rt,rt=a[rt].f){
splay(rt);
a[rt].son[1]=i;
pushup(rt);
}
}
inline void makeroot(int rt){access(rt);splay(rt);a[rt].flag^=1;}
int findroot(int rt){
access(rt);splay(rt);
while(a[rt].son[0])rt=a[rt].son[0];
return rt;
}
inline void split(int x,int y){makeroot(x);access(y);splay(y);}
inline void link(int x,int y){makeroot(x);a[x].f=y;}
inline void cut(int x,int y){
split(x,y);
if(a[y].son[0]==x&&a[x].f==y&&!a[x].son[1])a[x].f=a[y].son[0]=0;
}
inline void add_edge(int rt,int colour){
int x=MAP::a[rt].x,y=MAP::a[rt].y,end=MAP::a[rt].end,id=MAP::a[rt].id;
if(findroot(y)==findroot(x)){
split(x,y);
if(a[y].minn>end)return;
int pos=a[y].pos;
cut(pos,left[pos]);cut(pos,right[pos]);
clean(pos);
}
else ans[colour]--;
id+=MAXN-10;
a[id].val=end;a[id].cut=true;a[id].pos=id;
left[id]=x;right[id]=y;
link(id,x);link(id,y);
pushup(id);
}
inline void del_edge(int rt){
int x=MAP::a[rt].x,y=MAP::a[rt].y,id=MAP::a[rt].id;
id+=MAXN-10;
cut(id,x);cut(id,y);
a[id].cut=false;
}
}
void work(){
int now;
for(now=1;now<=MAP::num&&!MAP::a[now].start;now++)LCT::add_edge(now,MAP::a[now].colour);
for(int i=1;i<=m;i++){
int x=chess[que[i].x][que[i].y];
for(;now<=MAP::num&&MAP::a[now].start<=i;now++){
if(!MAP::a[now].flag){
if(!LCT::a[MAP::a[now].id+MAXN-10].cut)continue;
LCT::del_edge(now);
ans[x]++;
}
else LCT::add_edge(now,x^1);
}
ans[x]--;ans[x^1]++;
chess[que[i].x][que[i].y]^=1;
printf("%d %d\n",ans[1],ans[0]);
}
}
void init(){
int x,y,u,v,id;
n=read();
ans[0]=ans[1]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
chess[i][j]=backup[i][j]=read();
ans[chess[i][j]]++;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
x=MAP::point_id(i,j);
if(j!=n&&chess[i][j]==chess[i][j+1])MAP::add_edge(x,x+1,0,MAP::edge_id(i,j,i,j+1),chess[i][j],true);
if(i!=n&&chess[i][j]==chess[i+1][j])MAP::add_edge(x,x+n,0,MAP::edge_id(i,j,i+1,j),chess[i][j],true);
}
m=read();
for(int i=1;i<=m;i++){
que[i].x=read();que[i].y=read();
x=MAP::point_id(que[i].x,que[i].y);
for(int k=0;k<4;k++){
u=que[i].x+fx[k];v=que[i].y+fy[k];id=MAP::point_id(u,v);
if(MAP::check(u,v))continue;
if(backup[que[i].x][que[i].y]==backup[u][v])MAP::add_edge(x,id,i,MAP::edge_id(que[i].x,que[i].y,u,v),0,false);
else MAP::add_edge(x,id,i,MAP::edge_id(que[i].x,que[i].y,u,v),0,true);
}
backup[que[i].x][que[i].y]^=1;
}
MAP::build();
for(int i=1;i<=MAP::num;i++)MAP::a[i].end=m+1;
for(int i=MAP::num;i>=1;i--){
if(MAP::cut[MAP::a[i].id]!=-1)MAP::a[i].end=MAP::cut[MAP::a[i].id];
MAP::cut[MAP::a[i].id]=MAP::a[i].start;
}
for(int i=1;i<=MAXN-10;i++)LCT::a[i].val=MAX;
}
int main(){
init();
work();
return 0;
}