题目描述

  有一个\(n\times m\)的网格,线框的交点可以扭动,边不可伸缩。网格中有一些格子里面放了'x'形的支架,这些格子不会变形,但可以整体转动。如果所有格子都不能变形,那么称这个网格稳固。

  有\(q\)个操作,每次改变一个格子的状态,即有支架给为无支架,无支架改为有支架。

  请你判断初始及每次操作后这个网格是否稳固。

  比如说下面这个网格就不稳固。

  \(n,m\leq 3000,q\leq 100000\)

题解

  先看看怎么判断一个网格是否稳固。

  先给整个网格的左边和上边加上一行一列,然后把第一行的最左边两个格子设为有支架的格子,其他的设为没支架的格子。

  样例那个图就会变成这样

  可以发现这样操作是不会改变整个图形的稳定性的。

  设格子\((i,j)\)右下角的角度为\(a_{i,j}+90\)。

  因为一个交点四个角的度数和为\(360\),所以可以列出以下方程:

\[\begin{align}
90+a_{i-1,j-1}+180-90-a_{i-1,j}+180-90-a_{i,j-1}+90+a_{i,j}&=360\\
a_{i-1,j-1}+a_{i,j}-a_{i,j-1}-a_{i-1,j}&=0
\end{align}
\]

  然后通过一些简单变换可以得到

\[a_{0,0}+a_{i,j}-a_{0,j}-a_{i,0}=0
\]

  因为\(a_{0,0}=0\),所以方程简化为

\[a_{i,j}=a_{i,0}+a_{0,j}
\]

  当\((i,j)\)有支架时\(a_{i,0}+a_{0,j}=a_{i,j}=0\),即\(a_{i,0}=-a_{0,j}\),那么我们就在图\(G\)的\(i\)和\(j+n\)两个点之间连一条边。

  显然这个图是二分图。

  因为边界上只有\(a_{0,1}=0\),所以一个点只有和\(n+1\)号点\((0,1)\)属于同一个联通块,这个点对应的角的角度才是确定的。

  当\((i,j)\)无支架时\(a_{i.j}=a_{i,0}+a_{0,j}\)。如果\(a_{i,0}\)和\(a_{0,j}\)之间有一个没有确定,那么\(a_{i,j}\)也没有确定。

  所以说,这个网格是稳定的\(\Longleftrightarrow\)图\(G\)只有一个联通块。

  现在问题就变成了:有一个\(n+m\)个点的图,有\(nm\)条边,还有\(q\)个加边删边的操作。问操作前和每一次操作完后联通块个数是不是\(1\)。

  用分治+并查集和LCT都可以做。

  可以把一定存在的边先用路径压缩的并查集处理完。

  分治+并查集:\(O(nm\alpha+q\log^2(n+m))\)

  LCT:\(O(nm\alpha+q\log (n+m))\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<utility>
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
void open(const char *s)
{
#ifndef ONLINE_JUDGE
char str[100];
sprintf(str,"%s.in",s);
freopen(str,"r",stdin);
sprintf(str,"%s.out",s);
freopen(str,"w",stdout);
#endif
}
vector<pii> a[400010];
int f[10010];
int r[10010];
int ans[100010];
int s1[10010];//x
int s2[10010];//f[x]
int s3[10010];//r[f[x]]
int top;
int n,m,q;
char s[10010];
int c[3010][3010];
int find(int x)
{
return f[x]==x?x:find(f[x]);
}
int find2(int x)
{
return f[x]==x?x:f[x]=find(f[x]);
}
int num=0;
int merge(int x,int y)
{
x=find(x);
y=find(y);
if(x==y)
return 0;
if(r[x]>r[y])
swap(x,y);
top++;
s1[top]=x;
s2[top]=y;
s3[top]=r[y];
if(r[x]==r[y])
r[y]++;
f[x]=y;
return 1;
}
int merge2(int x,int y)
{
x=find(x);
y=find(y);
if(x==y)
return 0;
if(r[x]>r[y])
swap(x,y);
f[x]=y;
if(r[x]==r[y])
r[y]++;
return 1;
}
void back()
{
f[s1[top]]=s1[top];
r[s2[top]]=s3[top];
top--;
}
void add(int p,int l,int r,int x,int y,int L,int R)
{
if(l<=L&&r>=R)
{
a[p].push_back(pii(x,y));
return;
}
int mid=(L+R)>>1;
if(l<=mid)
add(p<<1,l,r,x,y,L,mid);
if(r>mid)
add((p<<1)|1,l,r,x,y,mid+1,R);
}
void solve(int l,int r,int p)
{
int now=top;
for(auto v:a[p])
if(merge(v.first,v.second))
num++;
if(l==r)
ans[l]=(num==n+m-1);
else
{
int mid=(l+r)>>1;
solve(l,mid,p<<1);
solve(mid+1,r,(p<<1)|1);
}
while(top>now)
{
back();
num--;
}
}
int main()
{
open("grid");
scanf("%d%d%d",&n,&m,&q);
memset(c,-1,sizeof c);
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
for(int j=1;j<=m;j++)
if(s[j]=='x')
c[i][j]=0;
}
for(int i=1;i<=n+m;i++)
{
f[i]=i;
r[i]=1;
}
int x,y;
for(int i=1;i<=q;i++)
{
scanf("%d%d",&x,&y);
if(~c[x][y])
{
add(1,c[x][y],i-1,x,y+n,0,q);
c[x][y]=-1;
}
else
c[x][y]=i;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(~c[i][j])
{
if(c[i][j])
add(1,c[i][j],q,i,j+n,0,q);
else
if(merge2(i,j+n))
num++;
}
for(int i=1;i<=n+m;i++)
find2(i);
solve(0,q,1);
for(int i=0;i<=q;i++)
if(ans[i])
printf("S\n");
else
printf("U\n");
return 0;
}

【XSY2741】网格 分治 LCT 并查集的更多相关文章

  1. 【CF938G】Shortest Path Queries(线段树分治,并查集,线性基)

    [CF938G]Shortest Path Queries(线段树分治,并查集,线性基) 题面 CF 洛谷 题解 吼题啊. 对于每个边,我们用一个\(map\)维护它出现的时间, 发现询问单点,边的出 ...

  2. 【BZOJ4025】二分图(线段树分治,并查集)

    [BZOJ4025]二分图(线段树分治,并查集) 题面 BZOJ 题解 是一个二分图,等价于不存在奇环. 那么直接线段树分治,用并查集维护到达根节点的距离,只计算就好了. #include<io ...

  3. 【bzoj4998】星球联盟 LCT+并查集

    题目描述 在遥远的S星系中一共有N个星球,编号为1…N.其中的一些星球决定组成联盟,以方便相互间的交流.但是,组成联盟的首要条件就是交通条件.初始时,在这N个星球间有M条太空隧道.每条太空隧道连接两个 ...

  4. 【bzoj2959】长跑 LCT+并查集

    题目描述 某校开展了同学们喜闻乐见的阳光长跑活动.为了能“为祖国健康工作五十年”,同学们纷纷离开寝室,离开教室,离开实验室,到操场参加3000米长跑运动.一时间操场上熙熙攘攘,摩肩接踵,盛况空前.为了 ...

  5. Dash Speed【好题,分治,并查集按秩合并】

    Dash Speed Online Judge:NOIP2016十联测,Claris#2 T3 Label:好题,分治,并查集按秩合并,LCA 题目描述 比特山是比特镇的飙车圣地.在比特山上一共有 n ...

  6. 【BZOJ2049】 [Sdoi2008]Cave 洞穴勘测 LCT/并查集

    两种方法: 1.LCT 第一次LCT,只有link-cut和询问,无限T,到COGS上找了数据,发现splay里的父亲特判出错了(MD纸张),A了,好奇的删了反转T了.... #include < ...

  7. 【BZOJ】2049: [Sdoi2008]Cave 洞穴勘测(lct/并查集)

    http://www.lydsy.com/JudgeOnline/problem.php?id=2049 bzoj挂了..在wikioi提交,,1A-写lct的速度越来越快了-都不用debug-- 新 ...

  8. BZOJ_2049_[Sdoi_2008]_Cave_洞穴勘测_(LCT/并查集)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=2049 给出一个森林,起始互不相连,现在有link和cut两种操作,问x,y是否在一棵树里. 分 ...

  9. 【BZOJ2959】长跑 (LCT+并查集)

    Time Limit: 1000 ms   Memory Limit: 256 MB Description 某校开展了同学们喜闻乐见的阳光长跑活动.为了能“为祖国健康工作五十年”,同学们纷纷离开寝室 ...

随机推荐

  1. OO最后一次作业

    终于开始最后一次作业了,是时候为这学期oo画一个圆满的局句号了. 回首这学期的OO经历,一路走来,经过了开始对面向对象的初步接触,然后就是充满痛苦回忆的多线程,接下来到了令人焦头烂额的规格设计,最后是 ...

  2. Git_GitHub详解

    Git和Github详细教程 一  概述 说到Git和Github,前几天我们知道微软以75亿美元收购全球最大的代码托管和写作平台GitHub,而GitHub是全球最大的代码仓库,很多开发人员都将代码 ...

  3. ACM/ICPC 2018亚洲区预选赛北京赛站网络赛D-80 Days--------树状数组

    题意就是说1-N个城市为一个环,最开始你手里有C块钱,问从1->N这些城市中,选择任意一个,然后按照顺序绕环一圈,进入每个城市会有a[i]元钱,出来每个城市会有b[i]个城市,问是否能保证经过每 ...

  4. ssm知识点总结

    项目名称:教育网—在线调查系统 项目总体流程图: 设计调查:调查-->包裹--->问题(增删改查) 1.调整包裹顺序 2.移动复制包裹 3.深度删除 创建调查流程分析: 主要生成surve ...

  5. 使用json读写文件中的数据

    把json的数据写入到文件中 import json with open('data.json','w+') as f: json.dump({"name":"张彪&qu ...

  6. ORA-12541:TNS:无监听程序 解决办法

    昨天我在一台win7笔记本中安装了oracle11g,然后打算用另一个win10的笔记本使用plsql developer局域网内连接访问oracle数据库.但是遇到ORA-12541:TNS:无监听 ...

  7. day 7-3 僵尸进程,孤儿进程与守护进程

    一.基本定义 正常情况下,子进程是通过父进程创建的,子进程在创建新的进程.子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束. 当一个 进程完成它的工作终止之后,它 ...

  8. 配置Google Gmail分类和过滤器

    简单的记两笔. 首先点击右上角的⚙️里面选择settings. 选择Filters and Blocked Addresses 在这个页面可以选择 create a new filter创建一个新的过 ...

  9. 使用python库xlsxwriter库来输出各种xlsx文件

    功能性的文章直接用几个最简单的实现表达: xlsxwriter库的核心就是其Workbook对象. 创建一个指定名字的xlsx文件: import xlsxwriter filename = '/Us ...

  10. Python魔法方法(magic method)细解几个常用魔法方法(下)

    接上文,再介绍最后几个常用的魔法方法. 关于__dict__: 先上个例子: class Test(object): fly = True def __init__(self, age): self. ...