在抗日战争期间,华北平原广大地区进行了大规模的隧道战。 一般来说,通过隧道连接的村庄排成一列。 除了两端,每个村庄都与两个相邻的村庄直接相连。
入侵者经常对一些村庄发动袭击并摧毁其中的部分隧道。 八路军指挥官要求最新的隧道和村庄连接状态。 如果某些村庄严重隔离,必须立即恢复连接!
Input
输入的第一行包含两个正整数n和m(n,m≤50,000),表示村庄和事件的数量。 接下来的m行中的每一行描述一个事件。
以下所示的不同格式描述了三种不同的事件:
D x:第x个村庄被毁。
Q x:指挥官询问第x个村庄与其直接或间接相关的村庄数量。
R:最后毁坏的村庄被重建了。
Output
按顺序输出每个指挥官询问的答案。
Sample Input
7 9
D 3
D 6
D 5
Q 4
Q 5
R
Q 4
R
Q 4
Sample Output
1
0
2
4
思路:
如果村庄x被摧毁,那与他相连的村庄就是0个,如果没有被摧毁那么就等于,x左边第一个被摧毁村庄到x右边第一个被摧毁村庄之间的村庄的数量,很容易就可以想到用线段树可以做,直接上代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=50010;
int n,m,a[N];//a[x]==1,表示x没有被摧毁
struct node
{
int l,r,sum;//sum区间[l,r]之间没被摧毁的村庄的数量
}tr[N<<2];
void pushup(int rt)
{
tr[rt].sum=tr[rt<<1].sum+tr[rt<<1|1].sum;
}
void build(int rt,int l,int r)
{
if(l==r) tr[rt]={l,r,1},a[l]=1;
else
{
tr[rt]={l,r};
int mid = l+r>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
}
void updata(int rt,int x,int d)
{
if(tr[rt].l==x&&tr[rt].r==x) a[x]=tr[rt].sum=d;
else
{
int mid =tr[rt].l+tr[rt].r>>1;
if(x<=mid) updata(rt<<1,x,d);
else updata(rt<<1|1,x,d);
pushup(rt);
}
}
int query(int rt,int l,int r)//查询[l,r]之间被被摧毁村庄的数量
{
if(tr[rt].l>=l&&tr[rt].r<=r) return tr[rt].sum;
int mid =tr[rt].l+tr[rt].r>>1,res=0;
if(l<=mid) res+=query(rt<<1,l,r);
if(r>mid) res+=query(rt<<1|1,l,r);
return res;
}
int query1(int rt,int x)//查询x左边第一个被摧毁的村庄的坐标
{
if(tr[rt].l==tr[rt].r) return (tr[rt].sum==1?0:tr[rt].l);//这个节点没被摧毁就返回0,最小的点,最终的答案可能在这个节点左或者右,返回0,一定不影响答案。
int mid= tr[rt].l+tr[rt].r>>1;
if(x>mid)
{
if(query(1,mid+1,x)==x-mid) return query1(rt<<1,mid);//如果mid到x都没被摧毁,第一个被摧毁的村庄一定在mid左边
else return max(query1(rt<<1,mid),query1(rt<<1|1,x));//否则取最大值,就是离x最近的
}
else
{
return query1(rt<<1,x);//x<=mid,一定在[tr[rt].l,x]
}
}
int query2(int rt,int x)//查询x右边第一个被摧毁的村庄
{
if(tr[rt].l==tr[rt].r) return (tr[rt].sum==1?n+1:tr[rt].l);//与上面一样,n+1不影响结果
int mid= tr[rt].l+tr[rt].r>>1;
if(x<=mid)
{
if(query(1,x,mid)==mid-x+1) return query2(rt<<1|1,mid+1);//一定在mid+1右边
else return min(query2(rt<<1,x),query2(rt<<1|1,mid+1));//在x右边就得取最小值
}
else
{
return query2(rt<<1|1,x);//在x以右
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
build(1,1,n);
stack<int> st;
while(m--)
{
char op[2];
scanf("%s",op);
int x;
if(*op=='D')
{
scanf("%d",&x);
st.push(x);//用栈存储被摧毁的村庄
updata(1,x,0);
}
else if(*op=='Q')
{
scanf("%d",&x);
if(a[x]) printf("%d
",query2(1,x)-1-query1(1,x));
else puts("0");
}
else if(st.size())
{
updata(1,st.top(),1);
st.pop();
}
}
}
}
看了个大佬的代码,思路是一样的,用红黑树去求只用了265ms,上面哪个是1800+ms,
#include<bits/stdc++.h>
using namespace std;
int n,m;
int main()
{
while(~scanf("%d%d",&n,&m))
{
char op[2];
int x;
stack<int> st; set<int> stt;//都保存被摧毁的村庄
stt.insert(0);//预处理边界,避免麻烦
stt.insert(n+1);
while(m--)
{
scanf("%s",op);
if(*op=='D')
{
scanf("%d",&x);
st.push(x);
stt.insert(x);//将x插入
}
else if(*op=='Q')
{
scanf("%d",&x);
if(stt.find(x)!=stt.end()) puts("0");//x没被摧毁
else
{
auto l=stt.upper_bound(x);//返回最小的大于x的被摧毁村庄,就是x右边第一个被摧毁村庄,lower_bound()是一样的因为stt里面没有x
auto r=l--;//再减1就是最大的小于x的被摧毁村庄
printf("%d
",*r-*l-1);
}
}
else if(st.size())
{
stt.erase(st.top());
st.pop();
}
}
}
return 0;
}