题目链接:Tunnel Warfare - HDU 1540 - Virtual Judge (ppsucxtt.cn)
摧毁村庄和重建村庄都比较容易,就是一个线段树的单点修改操作,关键是如何查询与一个村庄直接或间接相关的村庄数量。两个相邻村庄相关需要满足什么条件呢?条件就是他们都没有被摧毁,那么我们就可以类似得到 l,l+1,……,r这些村庄相关的条件就是他们的区间和等于r-l+1,于是我们就知道了如何判断一个区间的村庄是否相关,因此我们就可以二分判断一个村庄左边和右边与它相关的村庄数目,然后加起来减一即可(因为这个村庄被计算了两次)。
注意这道题目有个坑点,就是一个村庄可能被重复摧毁,举个例子,假如我们摧毁村庄的顺序是
1 2 2,那么我们修复的第一个村庄是2,第二个村庄还是2,最后一个修复的村庄才是1,这就是本题的坑点所在,其他就没什么了,下面是代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int N=1e6+10;
int l[N],r[N],sum[N],st[N];
bool vis[N];
void pushup(int id)
{
sum[id]=sum[id<<1]+sum[id<<1|1];
}
void build(int id,int L,int R)
{
sum[id]=0;l[id]=L;r[id]=R;
if(L==R)
{
sum[id]=1;
return ;
}
int mid=L+R>>1;
build(id<<1,L,mid);
build(id<<1|1,mid+1,R);
pushup(id);
return ;
}
int query_interval(int id,int L,int R)
{
if(l[id]>=L&&r[id]<=R) return sum[id];
int mid=l[id]+r[id]>>1;
int ans=0;
if(mid>=L) ans+=query_interval(id<<1,L,R);
if(mid+1<=R) ans+=query_interval(id<<1|1,L,R);
return ans;
}
void update_point(int id,int x,int val)
{
if(l[id]==r[id])
{
sum[id]=val;
return ;
}
int mid=l[id]+r[id]>>1;
if(x<=mid) update_point(id<<1,x,val);
else update_point(id<<1|1,x,val);
pushup(id);
return ;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
build(1,1,n);
for(int i=1;i<=n;i++)
vis[i]=false;
int x;
char op[5];
int tt=0;
while(m--)
{
scanf("%s",op);
if(op[0]=='D')
{
scanf("%d",&x);
if(!vis[x]) update_point(1,x,0);
vis[x]=true;//标记为被摧毁(可能会被重复加入)
st[++tt]=x;//记录最后毁坏的村庄
}
else if(op[0]=='Q')
{
scanf("%d",&x);
int ans=0;
if(vis[x])//如果村庄已经被摧毁,则直接输出0
{
printf("0\n");
continue;
}
int ll=1,rr=x;
//二分找左边与当前村庄连通的村庄个数(包括当前村庄)
while(ll<rr)
{
int mid=ll+rr>>1;
if(query_interval(1,mid,x)==x-mid+1) rr=mid;
else ll=mid+1;
}
ans+=x-ll+1;
//二分找右边与当前村庄连通的村庄个数(包括当前村庄)
ll=x,rr=n;
while(ll<rr)
{
int mid=ll+rr+1>>1;
if(query_interval(1,x,mid)==mid-x+1) ll=mid;
else rr=mid-1;
}
ans+=ll-x+1;
printf("%d\n",ans-1);//x这个位置被计算了两次
}
else
{
if(!tt) continue;//若当前无待修复的村庄就直接执行下一步操作
x=st[tt--];
if(!vis[x]) continue;//可能会重复修复
vis[x]=false;//标记为已修复
update_point(1,x,1);
}
}
}
return 0;
}