题解 CF750H【New Year and Snowy Grid】
SSerxhs
2021-05-29 01:26:06
感觉 sys 的复杂度是错的~~又懒得卡了~~,所以来写一篇。
注意一下题目中的**连通**指的是四连通,以下**连通**都表示八连通。定义 $(u,v)$ **几乎**连通为有一个方格与 $u,v$ 分别连通且 $u,v$ 不连通。同理定义连通块之间的几乎连通关系。
考虑如何什么情况下答案为 `NO`。
![](https://codeforces.com/predownloaded/6b/42/6b42f35a09415bdfc1227185226517ad1ea8c884.png)
from sys
如果如图在棋盘外面挂一圈蓝色的新障碍,那么左上到右下有路径当且仅当左下到右上连通。同理,有两条不相交路径等价于仅添加一块方块后左下到右上连通。
当然这里有个奇怪的 case 是添加在左上角,然而实际上并没有这种添加情况(因为左上角允许走两次),所以图应该改一下,蓝色应该少覆盖一格。
连通关系显然用可撤销并查集维护下就可以了。
设在每个询问已有障碍基础上添加障碍 $x$ 后左下和右上连通的所有合法 $x$ 的集合为 $S$,定义 $T$ 为与 $x$ 连通的格子集合,那么对于每组询问,设 $A$ 表示新的障碍,如果答案为 `NO` 且左下与右上不连通,有两种情况:
- 若 $A\cap T=B\neq \varnothing$,那么显然 $\forall y\in B$ 必定与左下(或右上)连通,且存在方格 $x$ 使得 $x$ 与 $y$ 几乎连通且 $x$ 与右上(或左下)连通。 枚举 $S'$ 的每个元素,判断其是否为 $y$ 即可。由于 $x$ 与 $y$ 几乎连通,对每个 $x$ 只有 $16$ 个可能,可以暴力判断是否与另一侧连通。
- 若 $A\cap T=\varnothing$,由于一开始答案为 `YES`,那么必定是初始图中存在几乎连通 连通块对 $(u,v)$,它们分别在这次询问中与左下和右上连通了。初始图中的几乎连通障碍对数目至多为 $16hw$(这一点与上一种情况证明一样),可以哈希保存。而这次询问中与左下和右上连通的连通块必定和新障碍连通,那么暴力枚举新障碍接触到的连通块就可以了。注意这里枚举的是连通块而不是障碍,因而复杂度是正确的。
```cpp
#include <bits/stdc++.h>
using namespace std;
const int N=1e3+10,M=N*N;
typedef unsigned int ui;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
std::mt19937 rnd(time(0));
inline int sj(int n)
{
unsigned int x=rnd();
return x%n+1;
}
#define rand fst
template<typename typC> void read(register typC &x)
{
register int c=getchar(),fh=1;
while ((c<48)||(c>57))
{
if (c=='-') {c=getchar();fh=-1;break;}
c=getchar();
}
x=c^48;c=getchar();
while ((c>=48)&&(c<=57))
{
x=x*10+(c^48);
c=getchar();
}
x*=fh;
}
template<typename typC, typename... Args> void read(typC &first, Args& ... args) {
read(first);
read(args...);
}
template<typename typC> void read(register typC *a,int num)
{
for (register int i=1;i<=num;i++) read(a[i]);
}
template<typename typC> void write(register typC x)
{
if (x<0) putchar('-'),x=-x;
static int st[100];
register int tp=1;
register typC y;st[1]=x%10;x/=10;
while (x) y=x/10,st[++tp]=x-y*10,x=y;++tp;
while (--tp) putchar(st[tp]|48);
}
template<typename typC> void write(register typC *a,register int num)
{
for (register int i=1;i<=num;i++) write(a[i]),putchar(i==num?10:32);
}
template<typename typC> typC ab(register typC x)
{
if (x<0) return -x;
return x;
}
set<ll> p;
set<int> s;
int f[M],rk[M],st[M][2];
int nn,tp,ntp,L;
void init(int nnn)
{
nn=nnn;
for (int i=1;i<=nn;i++) f[i]=i,rk[i]=1;
tp=0;
}
int getf(register int x)
{
while (f[x]!=x) x=f[x];
return x;
}
int getfa(int x)
{
if (f[x]==x) return x;
return f[x]=getfa(f[x]);
}
void uni(int x,int y)
{
x=getf(x);y=getf(y);
s.insert(x);
if (x==y) return;
s.insert(y);
if (rk[x]>rk[y]) swap(x,y);
st[++tp][0]=x;st[tp][1]=rk[y];
if (rk[x]==rk[y]) ++rk[y];
f[x]=y;
}
void fix()
{
ntp=tp;
}
void roll()
{
while (tp>ntp) rk[f[st[tp][0]]]=st[tp][1],f[st[tp][0]]=st[tp][0],--tp;
}
int dir[9][2]={{0,0},{1,0},{0,1},{1,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}};
int id[N][N],a[N][N],b[N][2];
int n,m,q,i,j,k,cnt,c,l,x,y,z;
inline bool av(int x,int y)
{
return x>=0&&x<=n&&y>=0&&y<=m;
}
int main()
{
read(n,m,q);
for (i=1;i<=n;i++)
{
c=getchar();
while (c!='.'&&c!='#') c=getchar();
a[i][1]=c=='#';
for (j=2;j<=m;j++) a[i][j]=getchar()=='#';
}
++n;++m;
for (i=3;i<=n;i++) a[i][0]=1;
for (i=1;i+2<m;i++) a[n][i]=1;
for (i=3;i<=m;i++) a[0][i]=1;
for (i=0;i+2<n;i++) a[i][m]=1;
for (i=0;i<=n;i++) for (j=0;j<=m;j++) id[i][j]=++cnt;
init(cnt);
for (i=0;i<=n;i++) for (j=0;j<=m;j++) if (a[i][j])
{
for (k=1;k<=8;k++) if (av(x=i+dir[k][0],y=j+dir[k][1])&&a[x][y]) f[getfa(id[i][j])]=getfa(id[x][y]);
}
for (i=1;i<=cnt;i++) getfa(i);
for (i=0;i<=n;i++) for (j=0;j<=m;j++) if (a[i][j]) for (k=-2;k<=2;k++) for (l=-2;l<=2;l++) if (av(x=i+k,y=j+l)&&a[x][y])
{
z=getfa(id[i][j]);c=getfa(id[x][y]);
if (z==c) continue;
p.insert((ll)z*cnt+c);
}
for (i=1;i<=cnt;i++) if (f[i]!=i) rk[f[i]]=2;
fix();
for (int ii=1;ii<=q;ii++)
{
roll();
read(L);
s.erase(s.begin(),s.end());
s.insert(getf(id[n][0]));s.insert(getf(id[0][m]));
for (i=1;i<=L;i++)
{
read(b[i]-1,2);a[b[i][0]][b[i][1]]=1;
}
if (a[1][1]||a[n-1][m-1])
{
puts("NO");
goto aaa;
}
for (i=1;i<=L;i++)
{
for (k=1;k<=8;k++) if (av(x=b[i][0]+dir[k][0],y=b[i][1]+dir[k][1])&&a[x][y]) uni(id[b[i][0]][b[i][1]],id[x][y]);
}
if (getf(id[n][0])==getf(id[0][m]))
{
puts("NO");
goto aaa;
}
for (auto u:s) if (getf(u)==getf(id[n][0])) for (auto v:s) if (getf(v)==getf(id[0][m])&&p.find((ll)u*cnt+v)!=p.end())
{
puts("NO");
goto aaa;
}
for (i=1;i<=L;i++) if (getf(id[x=b[i][0]][y=b[i][1]])==getf(id[n][0])) for (k=-2;k<=2;k++) for (l=-2;l<=2;l++) if (av(z=x+k,c=y+l)&&a[z][c]&&getf(id[z][c])==getf(id[0][m]))
{
puts("NO");
goto aaa;
}
for (i=1;i<=L;i++) if (getf(id[x=b[i][0]][y=b[i][1]])==getf(id[0][m])) for (k=-2;k<=2;k++) for (l=-2;l<=2;l++) if (av(z=x+k,c=y+l)&&a[z][c]&&getf(id[z][c])==getf(id[n][0]))
{
puts("NO");
goto aaa;
}
puts("YES");
aaa:;
for (i=1;i<=L;i++) a[b[i][0]][b[i][1]]=0;
fflush(stdout);
}
}
```