题目链接: Tunnel Warfare.
大致题意
有n个点排成一列,每个点与相邻的点互相连通。
现对区间【1,n】进行以下三种操作:
- 破坏点x的联通性
- 询问点x所处的最大连通区间的长度
- 恢复最后被破坏的点联通性
解题思路
难点
- 如何计算每个点的联通长度?
- 如何恢复最后破坏点的联通性?
解决方案
定义:左(右)最大联通长度:从区间最左端(右)向右(左)前进,能联通的最大长度。
(1) 对于操作2我们可以通过维护每个子区间的最大联通左长度与最大联通右长度,再将其合并得到父亲区间的最大联通左长度与最大联通右长度。然后查询就只需通过计算两个子区间分别的左侧与右侧最大连通长度得到。
维护
inline void pushup(Tunnel &fa,Tunnel &ls,Tunnel &rs)
{
fa.lmax = ls.lmax + (ls.lmax == ls.r-ls.l+1 ? rs.lmax : 0);
fa.rmax = rs.rmax + (rs.rmax == rs.r-rs.l+1 ? ls.rmax : 0);
}
inline void pushup(int rt)
{
pushup(a[rt],a[rt<<1],a[rt<<1|1]);
}
父亲的左最大联通长度就等于左儿子的左最大联通长度加上(),
如果左儿子的左最大联通长度等于左儿子区间长度,则()为右儿子的左最大联通长度;
否则为0,即与右儿子区间联通性断开。
同理得右。
如图:
查询
int query(int pos,int rt)
{
if(a[rt].l==a[rt].r)
return a[rt].lmax;
int mid=(a[rt].l+a[rt].r)/2;
Tunnel ls=a[rt<<1],rs=a[rt<<1|1];
if(pos <= mid)
{
if((ls.r-pos)<ls.rmax)
return ls.rmax+rs.lmax;
else
return query(pos,rt<<1);
}
else
{
if((pos-ls.r)<=rs.lmax)
return rs.lmax+ls.rmax;
else
return query(pos,rt<<1|1);
}
}
如果查询点落在左儿子区间中,则看它是否落在了右最长联通长度区间中。
若是,那么结果即为左儿子的右最长联通长度加上右儿子的左最长联通长度;
若不是,那么继续向下查询。
同理得右。
如图:
(2)对于操作3,我们可以通过栈后进先出的原理用stack解决。
(3)注意本题是多组输入
AC代码如下:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<stack>
using namespace std;
typedef long long ll;
const int N = 50005;
struct Tunnel
{
int l,r,lmax,rmax;
}a[N<<2];
inline void pushup(Tunnel &fa,Tunnel &ls,Tunnel &rs)
{
fa.lmax = ls.lmax + (ls.lmax == ls.r-ls.l+1 ? rs.lmax : 0);
fa.rmax = rs.rmax + (rs.rmax == rs.r-rs.l+1 ? ls.rmax : 0);
}
inline void pushup(int rt)
{
pushup(a[rt],a[rt<<1],a[rt<<1|1]);
}
void build(int lef, int rig, int rt)
{
a[rt] = {lef,rig,1,1};
if(lef==rig) return ;
int mid=(lef+rig)/2;
build(lef,mid,rt<<1);
build(mid+1,rig,rt<<1|1);
pushup(rt);
}
void update(int pos,int val,int rt)
{
if(a[rt].l==a[rt].r)
{
a[rt].lmax=a[rt].rmax=val;
return;
}
int mid=(a[rt].l+a[rt].r)/2;
if(pos<=mid) update(pos,val,rt<<1);
else update(pos,val,rt<<1|1);
pushup(rt);
}
int query(int pos,int rt)
{
if(a[rt].l==a[rt].r)
{
return a[rt].lmax;
}
int mid=(a[rt].l+a[rt].r)/2;
Tunnel ls=a[rt<<1],rs=a[rt<<1|1];
if(pos <= mid)
{
if((ls.r-pos)<ls.rmax)
{
return ls.rmax+rs.lmax;
}
else
return query(pos,rt<<1);
}
else
{
if((pos-ls.r)<=rs.lmax)
{
return rs.lmax+ls.rmax;
}
else
return query(pos,rt<<1|1);
}
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
stack<int> last;
build(1,n,1);
while(m--)
{
char s[2];
int x;
cin>>s;
if(s[0]=='D')
{
scanf("%d",&x);
update(x,0,1);
last.push(x);
}
else if(s[0]=='Q')
{
scanf("%d",&x);
printf("%d\n",query(x,1));
}
else
{
update(last.top(),1,1);
last.pop();
}
}
}
return 0;
}
总结
- 线段树的单点修改与区间查询
- 区间合并的维护
萌新的第一篇题解哈哈哈,如有不当之处,请多指出~