【题解】Luogu P4121 [WC2005]双面棋盘
原题传送门
这道题肥肠毒瘤qwqwq,我被卡了qwqwq
这题的正解好像是线段树+并查集,但由于我人丑常数大被卡成了70
#include <bits/stdc++.h>
#define N 205
#define getchar nc
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
register int x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register int x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
int n,m;
int a[N][N];
struct node{
int le[N],ri[N],lb[N],rb[N],s0,s1;
}t[N<<2];
int fa[N<<2],tmp[N<<2];
inline int father(register int x)
{
return fa[x]==x?x:fa[x]=father(fa[x]);
}
inline void pushup(register int x)
{
t[x].s0=t[x<<1].s0+t[x<<1|1].s0;
t[x].s1=t[x<<1].s1+t[x<<1|1].s1;
memcpy(t[x].lb,t[x<<1].lb,sizeof(t[x].lb));
memcpy(t[x].rb,t[x<<1|1].rb,sizeof(t[x].rb));
for(register int i=1;i<=n<<2;++i)
fa[i]=i;
for(register int i=1;i<=n;++i)
t[x<<1|1].le[i]+=n<<1,t[x<<1|1].ri[i]+=n<<1;
for(register int i=1;i<=n;++i)
{
int xx=t[x<<1].ri[i],yy=t[x<<1|1].le[i];
if(father(xx)!=father(yy)&&t[x<<1].rb[i]==t[x<<1|1].lb[i])
{
fa[father(xx)]=father(yy);
if(t[x<<1].rb[i])
--t[x].s1;
else
--t[x].s0;
}
}
for(register int i=1;i<=n;++i)
t[x].le[i]=father(t[x<<1].le[i]),t[x].ri[i]=father(t[x<<1|1].ri[i]);
for(register int i=1;i<=n;++i)
tmp[i<<1]=t[x].le[i],tmp[(i<<1)-1]=t[x].ri[i];
sort(tmp+1,tmp+1+(n<<1));
int maxsum=unique(tmp+1,tmp+1+(n<<1))-tmp-1;
for(register int i=1;i<=n;++i)
{
t[x].le[i]=lower_bound(tmp+1,tmp+1+maxsum,t[x].le[i])-tmp;
t[x].ri[i]=lower_bound(tmp+1,tmp+1+maxsum,t[x].ri[i])-tmp;
}
for(register int i=1;i<=n;++i)
t[x<<1|1].le[i]-=n<<1,t[x<<1|1].ri[i]-=n<<1;
}
inline void build(register int x,register int l,register int r)
{
if(l==r)
{
int tot=0;
for(register int i=1;i<=n;++i)
{
if(a[i][l]!=a[i-1][l])
{
++tot;
if(a[i][l])
++t[x].s1;
else
++t[x].s0;
}
t[x].le[i]=t[x].ri[i]=tot;
t[x].lb[i]=t[x].rb[i]=a[i][l];
}
return;
}
int mid=l+r>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
pushup(x);
}
inline void change(register int x,register int l,register int r,register int pos)
{
if(l==r)
{
int tot=0;
t[x].s0=t[x].s1=0;
for(register int i=1;i<=n;++i)
{
if(a[i][l]!=a[i-1][l])
{
++tot;
if(a[i][l])
++t[x].s1;
else
++t[x].s0;
}
t[x].le[i]=t[x].ri[i]=tot;
t[x].lb[i]=t[x].rb[i]=a[i][l];
}
return;
}
int mid=l+r>>1;
if(pos<=mid)
change(x<<1,l,mid,pos);
else
change(x<<1|1,mid+1,r,pos);
pushup(x);
}
int main()
{
n=read();
for(register int i=1;i<=n;++i)
{
a[0][i]=-1;
for(register int j=1;j<=n;++j)
a[i][j]=read();
}
build(1,1,n);
m=read();
while(m--)
{
int x=read(),y=read();
a[x][y]^=1;
change(1,1,n,y);
write(t[1].s1),putchar(' '),write(t[1].s0),puts("");
}
return 0;
}
好像珂以用lct维护连通性?
不用强制在线,我们珂以离线处理
计算每条边加入和删除的时间,就珂以用lct维护删除时间最靠后的生成树
代码(抄来的)
#include <bits/stdc++.h>
#define C 205
#define N 200005
#define getchar nc
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
register int x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register int x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int n,m,sz;
int color[C][C],c[C][C],ans[2];
struct data{
int x,y,t,d,id,opt,c;
bool operator < (const data &a) const
{
return x<a.x||x==a.x&&y<a.y;
}
}e[N],opr[10005];
inline bool cmp(register data a,register data b)
{
return a.t<b.t||(a.t==b.t&&a.opt<b.opt);
}
map <data,int> mp;
int cnt=0;
inline int eid(register int a,register int b,register int c,register int d)
{
if(a==c)
{
if(b>d)
b^=d^=b^=d;
return (a-1)*(n-1)+b;
}
else
{
if(a>c)
a^=c^=a^=c;
return n*(n-1)+(a-1)*n+b;
}
}
int f[N],ch[N][2],minn[N],rev[N],sta[N],val[N];
int re[N],pt[N],l[N],r[N],tree[N];
inline bool isroot(register int x)
{
return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;
}
inline int get(register int x)
{
return ch[f[x]][1]==x;
}
inline void update(register int x)
{
int loc=x;
if(ch[x][0])
{
if(val[minn[ch[x][0]]]<val[loc])
loc=minn[ch[x][0]];
}
if(ch[x][1])
{
if(val[minn[ch[x][1]]]<val[loc])
loc=minn[ch[x][1]];
}
minn[x]=loc;
}
inline void pushdown(register int x)
{
if(x&&rev[x])
{
if(ch[x][0])
rev[ch[x][0]]^=1;
if(ch[x][1])
rev[ch[x][1]]^=1;
ch[x][0]^=ch[x][1]^=ch[x][0]^=ch[x][1];
rev[x]=0;
}
}
inline void rotate(register int x)
{
int old=f[x],oldf=f[old],wh=get(x);
if(!isroot(old))
ch[oldf][ch[oldf][1]==old]=x;
f[x]=oldf;
ch[old][wh]=ch[x][wh^1];
if(ch[old][wh])
f[ch[old][wh]]=old;
ch[x][wh^1]=old;
f[old]=x;
update(old);
update(x);
}
inline void splay(register int x)
{
int top=0;
sta[++top]=x;
for(register int i=x;!isroot(i);i=f[i])
sta[++top]=f[i];
for(register int i=top;i;--i)
pushdown(sta[i]);
for(register int fa;!isroot(x);rotate(x))
if (!isroot(fa=f[x]))
rotate((get(x)==get(fa))?fa:x);
}
inline void access(register int x)
{
int t=0;
for(;x;t=x,x=f[x])
{
splay(x);
ch[x][1]=t;
update(x);
}
}
inline void reverse(register int x)
{
access(x);
splay(x);
rev[x]^=1;
}
inline int find(register int x)
{
access(x);
splay(x);
while(ch[x][0])
x=ch[x][0];
return x;
}
inline void link(register int x,register int y)
{
reverse(x);
f[x]=y;
}
inline void cut(register int x,register int y)
{
reverse(x);
access(y);
splay(y);
ch[y][0]=f[x]=0;
}
inline void add(register int i,register int cc)
{
int x=e[i].x,y=e[i].y,d=e[i].d,id=e[i].id;
if(find(x)==find(y))
{
reverse(x);
access(y);
splay(y);
int loc=minn[y];
if(d<=val[loc])
return;
cut(loc,l[loc]);
cut(loc,r[loc]);
tree[re[loc]]=0;
}
else
--ans[cc];
++sz;
val[sz]=d,re[sz]=id,pt[id]=sz,tree[id]=1;
l[sz]=x,r[sz]=y;
link(x,sz);
link(y,sz);
}
inline void del(register int i)
{
int x=e[i].x,y=e[i].y,id=e[i].id;
cut(x,pt[id]);
cut(y,pt[id]);
tree[id]=0;
}
int main()
{
n=read();
for(register int i=1;i<=n;++i)
for(register int j=1;j<=n;++j)
{
color[i][j]=c[i][j]=read();
++ans[c[i][j]];
}
for(register int i=1;i<=n;++i)
for(register int j=1;j<=n;++j)
{
int num=(i-1)*n+j,cc=color[i][j];
if(j!=n&&color[i][j+1]==cc)
e[++cnt].x=num,e[cnt].y=num+1,e[cnt].t=0,e[cnt].id=eid(i,j,i,j+1),e[cnt].opt=1,e[cnt].c=cc;
if(i!=n&&color[i+1][j]==cc)
e[++cnt].x=num,e[cnt].y=num+n,e[cnt].t=0,e[cnt].id=eid(i,j,i+1,j),e[cnt].opt=1,e[cnt].c=cc;
}
m=read();
for(register int i=1;i<=m;++i)
{
int x=read(),y=read();
opr[i].x=x,opr[i].y=y;
int num=(x-1)*n+y;
for(register int j=0;j<4;++j)
{
int nx=x+dx[j],ny=y+dy[j],nnum=(nx-1)*n+ny;
if(nx<=0||ny<=0||nx>n||ny>n)
continue;
if(c[x][y]==c[nx][ny])
e[++cnt].x=num,e[cnt].y=nnum,e[cnt].t=i,e[cnt].id=eid(x,y,nx,ny),e[cnt].opt=-1;
else
e[++cnt].x=num,e[cnt].y=nnum,e[cnt].t=i,e[cnt].id=eid(x,y,nx,ny),e[cnt].opt=1;
}
c[x][y]^=1;
}
sort(e+1,e+cnt+1,cmp);
for(register int i=1;i<=cnt;++i)
e[i].d=m+1;
mp.clear();
for(register int i=cnt;i>=1;--i)
{
if(e[i].x>e[i].y)
e[i].x^=e[i].y^=e[i].x^=e[i].y;
if(mp[e[i]])
e[i].d=mp[e[i]];
mp[e[i]]=e[i].t;
}
sz=n*n;
memset(val,127,sizeof(val));
int now=1;
for(;now<=cnt&&e[now].t<=0;++now)
add(now,e[now].c);
for(register int i=1;i<=m;++i)
{
int x=opr[i].x,y=opr[i].y;
int cc=color[x][y];
for(;now<=cnt&&e[now].t<=i;++now)
if(e[now].opt==-1)
{
if(!tree[e[now].id])
continue;
del(now);
++ans[cc];
}
else
add(now,cc^1);
--ans[cc],++ans[cc^1];
color[x][y]^=1;
write(ans[1]),putchar(' '),write(ans[0]),puts("");
}
return 0;
}
【题解】Luogu P4121 [WC2005]双面棋盘的更多相关文章
- P4121 [WC2005]双面棋盘
题目 P4121 [WC2005]双面棋盘 貌似是刘汝佳出的题目?? 做法 线段树维护并查集 线段树分治\(1\)~\(n\)行,我们要考虑维护的肯定是黑.白各自的联通块数量 考虑区间合并,其实就与中 ...
- 洛谷P4121 [WC2005]双面棋盘(线段树套并查集)
传送门 先膜一下大佬->这里 据说这题正解是LCT,然而感觉还是线段树套并查集的更容易理解 我们对于行与行之间用线段树维护,每一行内用并查集暴力枚举 每一行内用并查集暴力枚举连通块这个应该容易理 ...
- [WC2005]双面棋盘(并查集+分治)
题目描述 题解 唉,还是码力不行,写了一个多小时发现想错了又重构了一个多小时. 这道题意图很显然,动态维护联通块,有一个经典做法就是用LCT维护按照删除时间维护的最大生成树. 网上还有一种神奇的做法, ...
- [WC2005]双面棋盘
description 洛谷 给出一个\(n\times n\)的黑白棋盘. \(m\)次操作,每次将一个格子进行颜色翻转,求每次操作后的黑白四连通块数. data range \[n\le 200, ...
- [WC2005]双面棋盘(线段树+并查集)
线段树+并查集维护连通性. 好像 \(700ms\) 的时限把我的常数超级大的做法卡掉了, 必须要开 \(O_2\) 才行. 对于线段树的每一个结点都开左边的并查集,右边的并查集,然后合并. \(Co ...
- 【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][ ...
- [题解] Luogu P5446 [THUPC2018]绿绿和串串
[题解] Luogu P5446 [THUPC2018]绿绿和串串 ·题目大意 定义一个翻转操作\(f(S_n)\),表示对于一个字符串\(S_n\), 有\(f(S)= \{S_1,S_2,..., ...
- BZOJ1453: [WC2005]Dface双面棋盘
离线LCT维护MST,和3082的方法一样.然而比较码农,适合颓废的时候写. PS:线段树分治要好写得多,LCT比较自娱自乐. #include<bits/stdc++.h> using ...
随机推荐
- asch相关的linux基础知识分享
本文针对的人群:会用putty.SecureCRT.xhsell等工具ssh连接到自己的asch服务器上,但不怎么会执行命令的人.高手请绕路~ 本文主要围绕受托人搭建.维护涉及相关的内容进行Linux ...
- Python全栈-magedu-2018-笔记4
第三章 - Python 内置数据结构 元组tuple 一个有序的元素组成的集合 使用小括号 ( ) 表示 元组是不可变对象 元组的定义 初始化 定义 tuple() -> empty tupl ...
- [dev][dpdk][crypto] dpdk加解密设备与IPSEC
概述 分三部分,加解密框架(crypto framework),加解密设备(crypto dev),安全协议(Security Framework) × API,设计思路等,都在加解密框架里:见文档 ...
- mint-ui笔记
1.安装: npm install mint-ui --save npm install babel-plugin-component --save-dev //(只引入部分组件时需要安装) 2.导入 ...
- Android数据库无缝升级方案
软件迭代过程中,业务不断更新,也要求软件持续更新.相应地,数据库更新升级也是不可避免的一个环节.Android作为客户端应用,数据库升级相对于服务端来说会麻烦一些.常见的升级方式有: 1.删除旧表和数 ...
- Log4j配置和解释
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台.文件.GUI组件,甚至是套接口服务器.NT的事件记录器.UNIX Syslog守护进程等:我们也可 ...
- ASA failover
Active-Standby 1.作用:提供设备冗余 2.物理概念:primary 和 secondary ,需要命令敲得,角色不会切换, 3.虚拟概念:active和standby ,需要选举,角色 ...
- 聚合函数对NULL统计
Table ID Amount Number 1 28.26 NULL 2 NULL NULL 3 NULL NULL SQL SELECT SUM(Amount),SUM(Number),COUNT ...
- vue js判断长按触发及手指的上滑、下滑、左滑、又滑
<span class="btn" @touchstart="touchstart()" @touchmove="touchmove()&quo ...
- laravel----------Client error: `POST http://47.98.116.219/oauth/token` resulted in a `401 Unauthorized` response: {"error":"invalid_client","message":"Client authentication failed"}
1.设备没有授权,原因是 这个client_id的值就是数据库wk_oauth_clients 的主键ID,查看下表是否有这条数据