HDU 1540 / POJ 2892 Tunnel Warfare (单点更新,区间合并,求包含某点的最大连续个数)
题意:一条线上有n个点,D x是破坏这个点,Q x是表示查询x所在的最长的连续的点的个数,R是恢复上一次破坏的点。
思路:这题的关键是查询。
将被毁的村庄看成空位,当查询某个点的时候,如果我们知道它左边最近的空位a和右边最近的空位b,
那么我们只要查询区间[a,b]中的个数,即为答案,因为[a,b]之间不可能有空位存在了。
那么如何获取这样的a和b呢,这个就和HDU 4302 Holedox Eating 差不多了。
对每个节点,存储该区间中 空位的最大位置 和 空位的最小位置,还有 该区间村庄的个数。
这样当我们查询某点x的时候,那么我们只要查询[1,x]区间最大的空位编号a,[x,n]区间最小的空位编号为b,
那么a~b之间就没别的空位,查询[a,b]区间的村庄个数即为答案。
HDU 1540 比POJ 2892 要坑,如果在HDU 1540 要注意一下几点。
如下:
(1)多case
(2)某个村庄可以被毁坏多次(必须全部入栈),但只需要一次就能将其恢复(见下面的这组数据)
(3)D 3 D 2 D 1 D 1 D 2
R 恢复2
R 恢复1
R 恢复3
#include <iostream>
#include <stdio.h>
#include <set>
#include <string.h>
#define lson rt<<1,L,mid
#define rson rt<<1|1,mid+1,R using namespace std;
const int maxn=;
const int INF=0X3f3f3f3f;
int n,m;
int vis[maxn]; //vis[i]=1表示该村庄被毁
int last[maxn]; //栈存取被毁村庄的编号
int idx=-;
struct Node {
int sum; //该区间中村庄的个数
int maxval; //该区间中被毁村庄的最大编号
int minval; //该区间中被毁村庄的最小编号
} tree[maxn<<]; void build(int rt,int L,int R) {
tree[rt].sum=R-L+;
tree[rt].maxval=-INF;
tree[rt].minval=INF;
if(L==R)
return;
int mid=(L+R)>>;
build(lson);
build(rson);
}
void pushUp(int rt) {
tree[rt].sum=tree[rt<<].sum+tree[rt<<|].sum;
tree[rt].maxval=max(tree[rt<<].maxval,tree[rt<<|].maxval);
tree[rt].minval=min(tree[rt<<].minval,tree[rt<<|].minval);
} void update(int rt,int L,int R,int x,int c) {
if(L==R) {
tree[rt].sum=c;
//c=1表示恢复村庄,c=0表示摧毁该村庄
if(c) {
tree[rt].minval=INF;
tree[rt].maxval=-INF;
} else {
tree[rt].maxval=L;
tree[rt].minval=L;
}
return;
}
int mid=(L+R)>>;
if(x<=mid)
update(lson,x,c);
else
update(rson,x,c);
pushUp(rt);
}
//查询左区间被毁村庄的最大编号
int queryMax(int rt,int L,int R,int l,int r) {
if(l<=L&&R<=r) {
return tree[rt].maxval;
}
int ret;
int mid=(L+R)>>;
if(r<=mid)
ret=queryMax(lson,l,r);
else if(l>mid)
ret=queryMax(rson,l,r);
else {
ret=max(queryMax(lson,l,mid),queryMax(rson,mid+,r));
}
return ret;
}
//查询右区间被毁村庄的最小编号
int queryMin(int rt,int L,int R,int l,int r) {
if(l<=L&&R<=r) {
return tree[rt].minval;
}
int ret;
int mid=(L+R)>>;
if(r<=mid)
ret=queryMin(lson,l,r);
else if(l>mid)
ret=queryMin(rson,l,r);
else {
ret=min(queryMin(lson,l,mid),queryMin(rson,mid+,r));
}
return ret;
}
//查询该区间的村庄个数
int querySum(int rt,int L,int R,int l,int r) {
int ret=;
if(l<=L&&R<=r) {
return tree[rt].sum;
}
int mid=(L+R)>>;
if(l<=mid)
ret+=querySum(lson,l,r);
if(r>mid)
ret+=querySum(rson,l,r);
return ret;
}
int main() {
memset(vis,,sizeof(vis));
char str[];
int x,y,a;
while(scanf("%d%d",&n,&m)!=EOF) {
memset(vis,,sizeof(vis));
idx=-;
build(,,n);
for(int i=; i<=m; i++) {
scanf("%s",str);
if(str[]=='D') {
scanf("%d",&a);
update(,,n,a,);
last[++idx]=a; //不管之前是否被毁,都加入到栈
vis[a]=;
} else if(str[]=='R') {
/*
可能有的已经之前被修复了,所以这里要用while先判断一下
*/
while(vis[last[idx]]== &&idx>=)
idx--;
if(idx==-)
continue; //如果没有被毁的,则直接忽视就行
update(,,n,last[idx],);
vis[last[idx]]=;
idx--;
} else {
scanf("%d",&a);
if(vis[a]==)
printf("0\n");
else {
//x为a左边距离a最近的空位(即被拆的村庄)
//y为a右边距离a最近的空位(即被拆的村庄)
x=queryMax(,,n,,a);
y=queryMin(,,n,a,n);
//printf("%d,%d\n",x,y);
printf("%d\n",querySum(,,n,x,y)); //因为x、y之间没有被毁的村庄,所以查询x、y之间的村庄个数即可
}
}
}
}
return ;
}
还有另一个做法,那就是每个节点记录的是:左端开始连续的个数,右端开始连续的个数,最大的连续个数。
当查询某点x的时候,查询[1,x-1]区间,合并成t1节点;查询[x+1,n],合并成t2节点,答案即为t1.rsum+1+t2.lsum。
#include <iostream>
#include <stdio.h>
#include <set>
#include <string.h>
#define lson rt<<1,L,mid
#define rson rt<<1|1,mid+1,R using namespace std;
const int maxn=;
const int INF=0X3f3f3f3f;
int n,m;
int vis[maxn]; //vis[i]=1表示该村庄被毁,=0表示村庄没被毁
int last[maxn]; //栈存取被毁村庄的编号
int idx=-;
struct Node {
int lsum,msum,rsum; //左连续的最大值,连续的最大值,右连续的最大值
int len;
} tree[maxn<<]; void build(int rt,int L,int R) {
tree[rt].len=tree[rt].lsum=tree[rt].rsum=tree[rt].msum=R-L+;
if(L==R)
return;
int mid=(L+R)>>;
build(lson);
build(rson);
} void pushUp(Node &rt,Node &ls,Node &rs){
rt.lsum=ls.lsum;
rt.rsum=rs.rsum;
if(ls.lsum==ls.len)
rt.lsum+=rs.lsum;
if(rs.rsum==rs.len)
rt.rsum+=ls.rsum;
rt.msum=ls.rsum+rs.lsum;
rt.msum=max(rt.msum,max(ls.msum,rs.msum));
}
void update(int rt,int L,int R,int x,int c) {
if(L==R) {
//c=1表示恢复村庄,c=0表示摧毁该村庄
if(c) {
tree[rt].lsum=tree[rt].rsum=tree[rt].msum=;
} else {
tree[rt].lsum=tree[rt].rsum=tree[rt].msum=;
}
return;
}
int mid=(L+R)>>;
if(x<=mid)
update(lson,x,c);
else
update(rson,x,c);
pushUp(tree[rt],tree[rt<<],tree[rt<<|]);
}
//将对应区间[l,r]合并成一个节点返回
Node query(int rt,int L,int R,int l,int r){
if(l<=L&&R<=r){
return tree[rt];
}
int mid=(L+R)>>;
int length=(R-L+);
Node tmp,r1,r2;
if(r<=mid)
tmp=query(lson,l,r);
else if(l>mid)
tmp=query(rson,l,r);
else{
r1=query(lson,l,mid);
r2=query(rson,mid+,r);
tmp.len=r1.len+r2.len;
pushUp(tmp,r1,r2);
}
return tmp;
} int main() {
memset(vis,,sizeof(vis));
Node t1,t2;
char str[];
int a,ans;
while(scanf("%d%d",&n,&m)!=EOF) {
memset(vis,,sizeof(vis));
idx=-;
build(,,n);
for(int i=; i<=m; i++) {
scanf("%s",str);
if(str[]=='D') {
scanf("%d",&a);
update(,,n,a,);
last[++idx]=a; //不管之前是否被毁,都加入到栈
vis[a]=;
} else if(str[]=='R') {
/*
可能有的已经之前被修复了,所以这里要用while先判断一下
*/
while(vis[last[idx]]== &&idx>=)
idx--;
if(idx==-)
continue; //如果没有被毁的,则直接忽视就行
update(,,n,last[idx],);
vis[last[idx]]=;
idx--;
} else {
scanf("%d",&a);
if(vis[a]==)
printf("0\n");
else {
//为方便起见,1和n单独分出来
if(a==){
t1=query(,,n,a+,n);
ans=t1.lsum+;
}
else if(a==n){
t1=query(,,n,,a-);
ans=t1.rsum+;
}
else{
t1=query(,,n,,a-);
t2=query(,,n,a+,n);
ans=t1.rsum++t2.lsum;
}
printf("%d\n",ans);
}
}
}
}
return ;
}
HDU 1540 / POJ 2892 Tunnel Warfare (单点更新,区间合并,求包含某点的最大连续个数)的更多相关文章
- hdu 1540/POJ 2892 Tunnel Warfare 【线段树区间合并】
Tunnel Warfare Time Limit: 4000/2000 MS ...
- HDU 1540 POJ 2892 Tunnel Warfare
线段树 区间合并 单点修改 区间查询.又是1秒钟构思,差错查了好久... ... 发现一个int型的定义成了char型,打脸. #include <stdio.h> #include &l ...
- HDOJ(HDU).1166 敌兵布阵 (ST 单点更新 区间求和)
HDOJ(HDU).1166 敌兵布阵 (ST 单点更新 区间求和) 点我挑战题目 题意分析 根据数据范围和询问次数的规模,应该不难看出是个数据结构题目,题目比较裸.题中包括以下命令: 1.Add(i ...
- POJ 2892 Tunnel Warfare(线段树单点更新区间合并)
Tunnel Warfare Time Limit: 1000MS Memory Limit: 131072K Total Submissions: 7876 Accepted: 3259 D ...
- HDU 1540 Tunnel Warfare 线段树区间合并
Tunnel Warfare 题意:D代表破坏村庄,R代表修复最后被破坏的那个村庄,Q代表询问包括x在内的最大连续区间是多少 思路:一个节点的最大连续区间由(左儿子的最大的连续区间,右儿子的最大连续区 ...
- POJ 2892 Tunnel Warfare
传送门 很神奇的一道题,可以用线段树搞,为了练习treap所以拿treap写了. 其实根据询问,删除那个标号就加入平衡树,然后找到最大的和最小的就好了. 一些很烦人的小细节. //POJ 2892 / ...
- poj 2892 Tunnel Warfare(线段树)
Tunnel Warfare Time Limit: 1000MS Memory Limit: 131072K Total Submissions: 7499 Accepted: 3096 D ...
- HDU 3308 LCIS(线段树单点更新区间合并)
LCIS Given n integers. You have two operations: U A B: replace the Ath number by B. (index counting ...
- Tunnel Warfare 线段树 区间合并|最大最小值
B - Tunnel WarfareHDU - 1540 这个有两种方法,一个是区间和并,这个我个人感觉异常恶心 第二种方法就是找最大最小值 kuangbin——线段树专题 H - Tunnel Wa ...
随机推荐
- POJ 2287 Tian Ji -- The Horse Racing(贪心)
题意:田忌和齐王有n匹马,进行n局比赛,每局比赛输者给胜者200,问田忌最多能得多少钱. 分析:如果田忌最下等的马比齐王最下等的马好,是没必要拿最下等的马和齐王最好的马比的.(最上等马同理) 因此,如 ...
- adb连接不上手机
昨天电脑重装了系统,今天打开之前的eclips工作环境,点击run as -> android application,一直报各种诡异的错误,总之就是连接不上手机. 其中包括 Adb conne ...
- 《搭建DNS内外网的解析服务》RHEL6
首先说下: 搭建的这个dns内外网的解析,是正向解析,反向解析自己根据正向解析把文件颠倒下就ok了 第一步我们先搭建一个DNS的正反向解析(参考上篇DNS正反向解析,这是上篇做过的) 第二部才是搭建内 ...
- OrCAD Capture CIS与Allegro交互布局
激活OrCAD与Allegro的交互程序 1. 打开原题图,Options->Preference在Miscellaneous里勾选 2. 同时打开OrCAD原理图设计界面及Allegro PC ...
- 每天一个linux命令(1):which命令(转)
我们经常在linux要查找某个文件,但不知道放在哪里了,可以使用下面的一些命令来搜索: which 查看可执行文件的位置. whereis 查看文件的位置. locate ...
- 前端工程搭建NodeJs+gulp+bower
需要node.npm的事先安装!! 1.nodejs安装程序会在环境变量中添加两个变量: 系统环境变量中:path 增加C:\Program Files\nodejs\ 因为在该目下存在node.ex ...
- 如果在配置中将“system.serviceModel/serviceHostingEnvironment/multipleSiteBindingsEnabled”设置为 true,则需要终结点指定相对地址。如果在终结点上指定相对侦听 URI,则该地址可以是绝对地址。若要解决此问题,请为终结点“http://localhost/Service1.svc”指定相对 URI。
问题: 如果在配置中将"system.serviceModel/serviceHostingEnvironment/multipleSiteBindingsEnabled"设置为 ...
- openerp经典收藏 对象的预定义方法(转载)
对象的预定义方法 原文:http://shine-it.net/index.php/topic,2159.15.html 每个OpenERP的对象都有一些预定义方法,这些方法定义在基类osv.osv中 ...
- Python学习笔记1——人人都爱列表
一些BIF函数在列表中的应用: Python 3.3.4 (v3.3.4:7ff62415e426, Feb 10 2014, 18:13:51) [MSC v.1600 64 bit (AMD64) ...
- ASP.NET——拒绝访问。 (异常来自HRESULT:0x80070005 (E_ACCESSDENIED))
运行ASP.NET网站的时候出现下面这个问题,这个问题如图: 这个问题的解决方法为: 运行dcomcnfg 点 组件服务->服务->电脑->我的电脑->DCOM 配置 找到&q ...