这里是一条通往HDU的隧道
Tunnel WarfareTime Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 17974 Accepted Submission(s): 6942 Problem Description During the War of Resistance Against Japan, tunnel warfare was carried out extensively in the vast areas of north China Plain. Generally speaking, villages connected by tunnels lay in a line. Except the two at the ends, every village was directly connected with two neighboring ones.
Input The first line of the input contains two positive integers n and m (n, m ≤ 50,000) indicating the number of villages and events. Each of the next m lines describes an event.
Output Output the answer to each of the Army commanders’ request in order on a separate line.
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 |
地道战时期,每个村庄都是相连的,但是鬼子会来摧毁村庄,而八路则会去修复村庄,修复的是上一个鬼子破坏的村庄,给你指定一个村庄,问你和这个村庄连接的村庄有多少个?
学姐挂题的时候告诉我们说这道题可以暴力求解,嘻嘻,刚看到这个题的时候我想的也是暴力,暴力出奇迹嘛。结果我成功的暴毙~_~,后来才知道学姐说的是用二分暴力,不过我没写,还是练练我垃圾的线段树吧
其实这道题就是一个单点修改和区间查询的过程,一开始所有的村庄都是连接的,我们标记每个村庄为1,被摧毁之后我们就把它标记为0,求得连续为1的区间长度就行了呗
因为这是第一道线段树进阶的题目,所以说板子啥的都不知道,就有点费劲,其实有点底子和区间合并的意识都可以看得懂
先来说一下这种题的第一个板子吧:up向上更新函数
void up(ll k){
pp[k].lmx=pp[k<<1].lmx;
if(pp[k].lmx==pp[k<<1].r-pp[k<<1].l+1)//左边的全部连接 可以考虑一下右边
pp[k].lmx+=pp[k<<1|1].lmx;
//同理
pp[k].rmx=pp[k<<1|1].rmx;
if(pp[k].rmx==pp[k<<1|1].r-pp[k<<1|1].l+1)
pp[k].rmx+=pp[k<<1].rmx;
pp[k].mx=max(max(pp[k<<1].mx,pp[k<<1|1].mx),pp[k<<1].rmx+pp[k<<1|1].rmx);
}
摧毁和修复村庄都比较简单,单点修改即可,最难的就是区间查询部分
ll query(ll k,ll x){//查询与x点相邻的最长段
//点,区间长度为0,区间全部连接
if(pp[k].l==pp[k].r||pp[k].mx==0||pp[k].mx==pp[k].r-pp[k].l+1)
return pp[k].mx;
ll mid=(pp[k].l+pp[k].r)>>1;
if(x<=mid){//在左边的话,要注意最靠左或者中间
if(x>=mid-pp[k<<1].rmx+1)//靠近中间位置 ,且到中间都连续
return query(k<<1,x)+query(k<<1|1,mid+1);//向右延伸一下
else return query(k<<1,x);
}
else {//同理
if(x<=mid+pp[k<<1|1].lmx)
return query(k<<1|1,x)+query(k<<1,mid);
else return query(k<<1|1,x);
}
}
需要根据指定点的位置,来判断他能不能向两边延伸,在边上直接求解即可,中间部分一定要想着向对称轴子树的边界延伸
还有就是return边界的判断,鄙人不会,还是跟青大第二线段树大哥学的,可能过段时间就变成第一了吧,反正膜拜大佬就对了
Orz
还有就是看到这道题想到的第一个知识点,用栈来保存被摧毁的村庄,嘻嘻嘻
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<stack>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const ll inf=0x3f3f3f3f;
const ll mm=5e4+10;
stack<int>s;
struct node{
ll l,r,lmx,rmx,mx;
}pp[mm<<2];
void up(ll k){
pp[k].lmx=pp[k<<1].lmx;
if(pp[k].lmx==pp[k<<1].r-pp[k<<1].l+1)//左边的全部连接 可以考虑一下右边
pp[k].lmx+=pp[k<<1|1].lmx;
//同理
pp[k].rmx=pp[k<<1|1].rmx;
if(pp[k].rmx==pp[k<<1|1].r-pp[k<<1|1].l+1)
pp[k].rmx+=pp[k<<1].rmx;
pp[k].mx=max(max(pp[k<<1].mx,pp[k<<1|1].mx),pp[k<<1].rmx+pp[k<<1|1].rmx);
}
void build(ll k,ll l,ll r){
pp[k].l=l;
pp[k].r=r;
pp[k].lmx=pp[k].rmx=pp[k].mx=r-l+1;//
if(l==r)return ;
ll mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
up(k);
}
void change(ll k,ll x,ll y){//将x位置的数改成y
if(pp[k].l==pp[k].r){
pp[k].lmx=pp[k].rmx=pp[k].mx=y;
return ;
}
ll mid=(pp[k].l+pp[k].r)>>1;
if(x<=mid)
change(k<<1,x,y);
else change(k<<1|1,x,y);
up(k);
}
//
ll query(ll k,ll x){//查询与x点相邻的最长段
//点,区间长度为0,区间全部连接
if(pp[k].l==pp[k].r||pp[k].mx==0||pp[k].mx==pp[k].r-pp[k].l+1)
return pp[k].mx;
ll mid=(pp[k].l+pp[k].r)>>1;
if(x<=mid){//在左边的话,要注意最靠左或者中间
if(x>=mid-pp[k<<1].rmx+1)//靠近中间位置 ,且到中间都连续
return query(k<<1,x)+query(k<<1|1,mid+1);//向右延伸一下
else return query(k<<1,x);
}
else {//同理
if(x<=mid+pp[k<<1|1].lmx)
return query(k<<1|1,x)+query(k<<1,mid);
else return query(k<<1|1,x);
}
}
int main()
{
ll n,m;
while(cin>>n>>m){
build(1,1,n);
char op[2];
ll x;
while(m--){
cin>>op;
if(op[0]=='D'){
cin>>x;
s.push(x);
change(1,x,0);
}
else if(op[0]=='R'){
ll last=s.top();
s.pop();
change(1,last,1);
}
else {
cin>>x;
cout<<query(1,x)<<endl;
}
}
}
return 0;
}