题解:P17066 [ICPC 2017 Shenyang R] Bridge
lailai0916 · · 题解
题意简述
动态维护一张
解题思路
称连接同一列两个结点的边为竖边,连接同一行相邻列的边为横边。
梯形图中的简单环一定由两条竖边和它们之间上下两条完整横链组成。事实上,环每次经过竖边都会切换所在行。若切换超过两次,某一行中用于连接这些竖边的区间会相互覆盖,环便会重复经过结点。因此,判断一条边是否为桥,只需判断它能否处于这样的矩形环上。
把当前存在的竖边按列排序。考虑两条相邻竖边
若
第一条竖边左侧和最后一条竖边右侧不能缺少横边,否则缺口外侧的一段结点没有任何竖边可用于换行,也会与整张图断开。
因此,每个相邻竖边区间只需要记录一个状态:没有缺口,或者唯一缺失横边的位置。缺口位于哪一行不影响桥的数量。
在有序集合中加入哨兵
先计算区间内横边的贡献
- 若区间接触哨兵,所有
2(r-l-1) 条实际横边都不在任何环上,因此全是桥; - 若两端都是真实竖边且没有缺口,两条完整横链与端点竖边构成环,横边贡献为
0 ; - 若区间内有一个缺口,剩余
2(r-l)-1 条横边都无法进入矩形环,因此全是桥。
于是:
再计算真实竖边
定义区间为阻断区间,当且仅当它接触哨兵或含有缺口。设
总桥数就是所有相邻竖边区间的
一次修改只会改变常数个局部贡献。
修改横边时,先找到它所在的相邻竖边区间
加入列
删除竖边执行相反过程。区间
初始图的所有区间都完整。当
有序集合用升序提示插入,可在线性时间内初始化。每次修改只进行常数次集合操作,时间复杂度为
参考代码
#include <bits/stdc++.h>
using namespace std;
const int N=200005;
int n,ans;
int gap[N];
set<int> s;
bool broken(int l,int r)
{
return l==0||r==n+1||gap[l]!=0;
}
int horizontal(int l,int r)
{
if(l==0||r==n+1)return 2*(r-l-1);
if(gap[l])return 2*(r-l)-1;
return 0;
}
int vertical(int x)
{
if(x==0||x==n+1)return 0;
auto it=s.find(x);
return broken(*prev(it),x)&&broken(x,*next(it));
}
void add_vertical(int x)
{
auto it=s.lower_bound(x);
int l=*prev(it);
int r=*it;
ans-=horizontal(l,r)+vertical(l)+vertical(r);
int k=gap[l];
gap[l]=gap[x]=0;
if(k<x)gap[l]=k;
else gap[x]=k;
s.insert(x);
ans+=horizontal(l,x)+horizontal(x,r)+vertical(l)+vertical(x)+vertical(r);
}
void del_vertical(int x)
{
auto it=s.find(x);
int l=*prev(it);
int r=*next(it);
ans-=horizontal(l,x)+horizontal(x,r)+vertical(l)+vertical(x)+vertical(r);
if(!gap[l])gap[l]=gap[x];
gap[x]=0;
s.erase(it);
ans+=horizontal(l,r)+vertical(l)+vertical(r);
}
void change_horizontal(int op,int x)
{
auto it=s.upper_bound(x);
int l=*prev(it);
int r=*it;
ans-=horizontal(l,r)+vertical(l)+vertical(r);
if(op==1)gap[l]=0;
else gap[l]=x;
ans+=horizontal(l,r)+vertical(l)+vertical(r);
}
void solve()
{
int m;
cin>>n>>m;
fill(gap,gap+n+2,0);
s.clear();
for(int i=0;i<=n+1;i++)s.insert(s.end(),i);
ans=n==1;
while(m--)
{
int op,x,y,u,v;
cin>>op>>x>>y>>u>>v;
if(x!=u)
{
if(op==1)add_vertical(y);
else del_vertical(y);
}
else change_horizontal(op,min(y,v));
cout<<ans<<'\n';
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin>>T;
while(T--)solve();
return 0;
}