在抗日战争期间,华北平原广大地区进行了大规模的隧道战。一般来说,通过隧道连接的村庄排成一列。除了两端,每个村庄都与两个相邻的村庄直接相连。
入侵者经常对一些村庄发动袭击并摧毁其中的部分隧道。八路军指挥官要求最新的隧道和村庄连接状态。如果某些村庄严重隔离,必须立即恢复连接!
输入
输入的第一行包含两个正整数n和m(n,m≤50,000),表示村庄和事件的数量。接下来的m行中的每一行描述一个事件。
以下所示的不同格式描述了三种不同的事件:
D x:第x个村庄被摧毁。
问题x:陆军指挥部要求第x个村庄与其直接或间接相关的村庄数量。
R:最后毁坏的村庄被重建了。
产量
按顺序输出每个陆军指挥官要求的答案。
样本输入
7 9
D 3
D 6
D 5
Q 4
Q 5
R
Q 4
R
Q 4
样本输出
1
0
2
4
思路:
该题题意就是让你找点的连通的最大区间,网上由博客用区间合并做的,具体思路没看懂,也可以用最值解决;
例如 1 2 3 4 5,如果首先摧毁3村子,则12联通,45连通,与3连通的村子为0,我们维护两个区间最值,一个最大值,一个最小值,先将所有村子赋为0,哪个村子被摧毁,将该村子权值赋为该村编号,将维护的线段树最小值初始化为n+1,最大值初始化为0,同时摧毁时将该村子最大值最小值都赋为编号,然后最大连通区间即为询问的点右侧的最小值减去左侧最小值减一,如果相等则为0,,,看上面例子,初始化以后为 0 0 0 0 0 ,min:(6 6 6 6 6)max:(0 0 0 0 0),假如先将3摧毁,变为 0 0 3 0 0 ,min(6 6 3 6 6),max(0 0 3 0 0),如果查询3村子的连通数量,即min=min(3-5)即(3 6 6)的最小值为3,max=max(1-3)即(0 0 3)的最大值为3,相等结果为0,如果查询2村子的连通数量,即min=min(2-5)即(6 3 6 6 )的最小值为3,max(1-2)即(0 0)的最大值为0,结果为min-max-1=2;
这篇博客讲的很详细:https://blog.csdn.net/chudongfang2015/article/details/52133243
kuangbin线段树(vj)ac代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;
const int INF=1e9+7;
struct node
{
ll ma,mi;
}sum[maxn<<2];
ll n;
void build(ll root,ll l,ll r)
{
if(l==r)
{
sum[root].ma=0; //最大值赋为0,最小值赋为n+1,若查询的村子后面全没有被摧毁时,只能查道自己
sum[root].mi=n+1;
return ;
}
ll mid=(l+r)>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
sum[root].ma=max(sum[root<<1].ma,sum[root<<1|1].ma);
sum[root].mi=min(sum[root<<1].mi,sum[root<<1|1].mi);
}
void changemax(ll root,ll l,ll r,ll w,ll k) //更新区间最大值
{
if(l==r)
{
sum[root].ma=k;
return ;
}
ll mid=(l+r)>>1;
if(w<=mid) changemax(root<<1,l,mid,w,k);
else changemax(root<<1|1,mid+1,r,w,k);
sum[root].ma=max(sum[root<<1].ma,sum[root<<1|1].ma);
}
void changemin(ll root,ll l,ll r,ll w,ll k) //更新区间最小值
{
if(l==r)
{
sum[root].mi=k;
return ;
}
ll mid=(l+r)>>1;
if(w<=mid) changemin(root<<1,l,mid,w,k);
else changemin(root<<1|1,mid+1,r,w,k);
sum[root].mi=min(sum[root<<1].mi,sum[root<<1|1].mi);
}
ll qmax(ll root,ll l,ll r,ll ql,ll qr) //求区间最大值
{
if(ql<=l&&qr>=r)
{
return sum[root].ma;
}
ll mid=(l+r)>>1;
ll ans=-INF;
if(ql<=mid) ans=max(ans,qmax(root<<1,l,mid,ql,qr));
if(qr>mid) ans=max(ans,qmax(root<<1|1,mid+1,r,ql,qr));
return ans;
}
ll qmin(ll root,ll l,ll r,ll ql,ll qr) //求区间最小值
{
if(ql<=l&&qr>=r)
return sum[root].mi;
ll mid=(l+r)>>1;
ll ans=INF;
if(ql<=mid) ans=min(ans,qmin(root<<1,l,mid,ql,qr));
if(qr>mid) ans=min(ans,qmin(root<<1|1,mid+1,r,ql,qr));
return ans;
}
int main()
{
ll m;
ll h[maxn];
while(~scanf("%lld%lld",&n,&m))
{
ll cnt=1;
memset(h,0,sizeof(h));
memset(sum,0,sizeof(sum));
build(1,1,n);
for(int i=1;i<=m;i++)
{
char c[10];
ll k;
scanf("%s",c);
if(c[0]=='D')
{
scanf("%lld",&k);
changemax(1,1,n,k,k); //一个村子被摧毁时,把该村子由0变为该村子的编号
changemin(1,1,n,k,k);
h[cnt++]=k;
}
else if(c[0]=='Q')
{
scanf("%lld",&k);
ll ans1,ans2;
ans1=qmax(1,1,n,1,k); //记录1-k的最大值和k-n的最大值
ans2=qmin(1,1,n,k,n);
if(ans1==ans2)
printf("0\n");
else
printf("%lld\n",ans2-ans1-1);
}
else if(c[0]=='R')
{
ll ans=h[--cnt]; //更新在最新被摧毁的村子
changemax(1,1,n,ans,0);
changemin(1,1,n,ans,n+1);
}
}
}
return 0;
}