在抗日戰爭期間,華北平原廣大地區進行了大規模的隧道戰。 一般來說,通過隧道連接的村莊排成一列。 除了兩端,每個村莊都與兩個相鄰的村莊直接相連。
入侵者經常對一些村莊發動襲擊並摧毀其中的部分隧道。 八路軍指揮官要求最新的隧道和村莊連接狀態。 如果某些村莊嚴重隔離,必須立即恢復連接!
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\n",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\n",*r-*l-1);
}
}
else if(st.size())
{
stt.erase(st.top());
st.pop();
}
}
}
return 0;
}