题意:CS召唤了n个实验怪兽,第i号怪兽在i这个位置出。并把KI召唤出的第i位从者安排在pos(i)处,总共有m位从者。

第i只怪兽有战斗力atk(i), 而i号从者的体力为AP(i)。如果从者想要移动,他必须战胜他当前位置处的怪兽,战胜的条件为AP>=atk, 然后该从者的AP会减少atk, 注意从者只能从i移动到i+1,或移动到i-1处,注意一旦开始移动就只能向一个方向移动了。

CS会给出Q次询问,每次给出一个区间[l, r]: 要求KI只能派出一个从者,并能打败这个区间中尽可能多的怪物,求能打败的最大怪物数,强制在线

请求出只派出一位从者的情况下在[pl,pr]这个区间中所能战胜的最大怪物数

题解:开始就有个比较好的想法,但是总有一个小问题无法解决,然后突然之间想通了!!!

首先题目不要求修改,所以我们预处理每位从者可以到达的左与右的最远位置。从者只能一路向左或向右而atk>=0,因此根据前缀和的单调性二分找到左与右的最远的位置(我用的是map,注意设置哨兵减少边界判断),接着左右各扫一遍找到每个点向左向右最远扩展的距离

然后问题转化为:给一个区间,问此区间内可每个点可扩展距离的最大值是多少(注意从者可能不在这个区间内,但是移动进这个区间),但是这儿有一个问题就是:需要保证每个点向两边扩展不得超过给定的区间范围,而超过的部分是无效的,因此不能直接使用线段树维护区间最值

但是这个有个隐藏的条件就是(向右的情况几乎一样):每个点向左可扩展的距离一定是大于等于他右边最近的一个减一,所以当位置3可扩展距离为3时,向左超过了区间[2,10]后,则2这个位置也一定会超过,因此我们只需要3之后的就好了,这样在给定区间中,就找到最右边一个向左扩展刚好大于等于左边界的那个位置,接着他左边的值就可以不计算,右边的值利用线段树找区间最大值,他这个位置直接找到左边界的值(注意没有这个位置的情况)。这个位置我们需要将求出向左扩展的最大距离从左到右的位置依次减去1,2,3...,再利用线段树维护(经典),因为向左扩展时从左到右每个值比前一个值到左边界的距离要大一个,因此我们这样减去后,就直接求区间内最左边一个大于等于

-pl+1)的位置

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<iomanip>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define eps 1E-8
/*注意可能会有输出-0.000*/
#define Sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型
#define Cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化
#define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0
#define mul(a,b) (a<<b)
#define dir(a,b) (a>>b)
typedef long long ll;
typedef unsigned long long ull;
const int Inf=<<;
const ll INF=1ll<<;
const double Pi=acos(-1.0);
const int Mod=1e9+;
const int Max=<<;
int Segtrl[Max],Segtrr[Max],maxl[Max],maxr[Max];//两颗线段树
int lef[Max],rig[Max];//每个点向左向右可以到的最大距离
int atk[Max],pos[Max],ap[Max];
ll dig1[Max],dig2[Max];//左右前缀和
int n;
map<ll,int> suml,sumr;
void Solve(int m)//处理lef与rig
{
memset(lef,,sizeof(lef));
memset(rig,,sizeof(rig));
for(int i=; i<=m; ++i) //找到每个人可以到的左右最大距离
{
int posx=pos[i]; ll now=dig2[posx+]+ap[i];
lef[posx]=max(lef[posx],posx-(suml.upper_bound(now)->second));//找到大于now的最近的位置
//printf("%lld\n",now); now=dig1[posx-]+ap[i];
rig[posx]=max(rig[posx],(sumr.upper_bound(now)->second)-posx); //printf("OK%d %d %d\n",posx,lef[posx],rig[posx]);
}
for(int i=; i<=n; ++i) //枚举n找到每个位置可以到的最大距离
{
rig[i]=max(rig[i],rig[i-]-);
}
for(int i=n; i>; --i)
{
lef[i]=max(lef[i],lef[i+]-);
}
// for(int i=1;i<=n;++i)
// printf("%d %d\n",lef[i],rig[i]);
return;
}
void Upnow(int now,int next)
{
Segtrl[now]=max(Segtrl[next],Segtrl[next|]);
Segtrr[now]=max(Segtrr[next],Segtrr[next|]);
maxl[now]=max(maxl[next],maxl[next|]);
maxr[now]=max(maxr[next],maxr[next|]);
return ;
}
void Create(int sta,int enn,int now)
{
if(sta==enn)
{
Segtrl[now]=lef[sta];
Segtrr[now]=rig[sta]; maxl[now]=lef[sta]-sta;//关键 maxr[now]=rig[sta]-(n-sta+);//关键
return;
}
int next=(now<<);
int mid=(sta+enn>>);
Create(sta,mid,next);
Create(mid+,enn,next|);
Upnow(now,next);
return;
}
int flag;
int Update(int sta,int enn,int now,int x,int y,int z,int hh)
{
if(sta==enn)
{
flag=;
return sta;
}
int res;
int next=(now<<);
int mid=(sta+enn>>);
if(!hh)//最左边
{
res=n+;
if(!flag&&mid>=x&&maxr[next]>=z)
res=min(res,Update(sta,mid,next,x,y,z,hh));
if(!flag&&mid<y&&maxr[next|]>=z)
res=min(res,Update(mid+,enn,next|,x,y,z,hh));
}
else
{
res=;
if(!flag&&mid<y&&maxl[next|]>=z)
res=max(res,Update(mid+,enn,next|,x,y,z,hh));
if(!flag&&mid>=x&&maxl[next]>=z)
res=max(res,Update(sta,mid,next,x,y,z,hh));
}
return res;
}
int Query(int sta,int enn,int now,int x,int y,int hh)
{
if(sta>=x&&enn<=y)
{
//printf("OK%d %d\n",sta,enn);
if(!hh)
return Segtrr[now];
return Segtrl[now];
}
int next=(now<<);
int mid=(sta+enn>>);
int res=;
if(mid>=x)
res=max(res,Query(sta,mid,next,x,y,hh));
if(mid<y)
res=max(res,Query(mid+,enn,next|,x,y,hh));
return res;
}
int main()
{
int t,m,q;
scanf("%d",&t);
while(t--)
{
suml.clear();
sumr.clear();
scanf("%d %d %d",&n,&m,&q);
dig1[]=0ll;
for(int i=; i<=n; ++i)
{
scanf("%d",&atk[i]);
dig1[i]=dig1[i-]+atk[i];//前缀和
if(!sumr.count(dig1[i]))//仔细想想
sumr[dig1[i]]=i;//放入map
}
dig2[n+]=0ll;
for(int i=n; i>; --i)
{
dig2[i]=dig2[i+]+atk[i];
if(!suml.count(dig2[i]))
suml[dig2[i]]=i;
}
dig1[n+]=INF;
dig2[]=INF;
sumr[0ll]=;
suml[0ll]=n+;
suml[INF]=;
sumr[INF]=n+;//以上都为添加两个哨兵
for(int i=; i<=m; ++i)
{
scanf("%d",&pos[i]);
}
for(int i=; i<=m; ++i)
{
scanf("%d",&ap[i]);
}
Solve(m);
Create(,n,);
int tmp=,manx;
int pl,pr;
while(q--)
{
manx=;
scanf("%d %d",&pl,&pr);
pl^=tmp;
pr^=tmp;
if(pl>pr)
swap(pl,pr);
pl=max(pl,);
pr=min(pr,n);
//printf("%d %d\n",pl,pr);
flag=;
int posl=Update(,n,,pl,pr,pr-n,);//找最左边的位置(向右扩展的)
if(posl>pr)
manx=max(manx,Query(,n,,pl,pr,));//printf("%d %d\n",pl,posl);
else if(posl>pl)
manx=max(manx,max(Query(,n,,pl,posl-,),pr-posl+));//posl这个位置一定是可以扩展到pr的
else
manx=max(manx,pr-posl+);
flag=;
int posr=Update(,n,,pl,pr,-pl+,);//printf("%d %d\n",posr,pr);
if(posr<pl)
manx=max(manx,Query(,n,,pl,pr,));
else if(posr<pr)
manx=max(manx,max(Query(,n,,posr+,pr,),posr-pl+));
else
manx=max(manx,posr-pl+);
printf("%d\n",manx);
tmp=manx;
}
}
return ;
}

ZSTU 4241 圣杯战争(线段树+经典)的更多相关文章

  1. csu 1555(线段树经典插队模型-根据逆序数还原序列)

    1555: Inversion Sequence Time Limit: 2 Sec  Memory Limit: 256 MBSubmit: 469  Solved: 167[Submit][Sta ...

  2. POJ2155 Matrix二维线段树经典题

    题目链接 二维树状数组 #include<iostream> #include<math.h> #include<algorithm> #include<st ...

  3. poj_2528 Mayor's posters (线段树经典题+离散化方法)

    由于题面中给定的wall最大长度为10 000 000:若简单用线段树势必会超时. 而注意到题面中规定了输入海报的个数<=1000:因此不妨离散化,使得线段中叶节点仅含有1000个,那么线段最大 ...

  4. ZSTU 4241 圣杯战争(ST表+二分)

    题目链接  ZSTU 4241 问题转化为有很多区间,现在每次给定一个区间求这个区间和之前所有区间中的某一个的交集的最大长度. 强制在线. 首先我们把所有的区间预处理出来. 然后去重(那些被包含的小区 ...

  5. POJ 2155 二维线段树 经典的记录所有修改再统一遍历 单点查询

    本来是想找一个二维线段树涉及懒惰标记的,一看这个题,区间修改,单点查询,以为是懒惰标记,敲到一半发现这二维线段树就不适合懒惰标记,你更新了某段的某列,但其实其他段的相应列也要打标记,但因为区间不一样, ...

  6. 【poj2182】【poj2828】树状数组/线段树经典模型:逆序查找-空位插入法

    poj2182题意:有一个1~n的排列,现在给定每个人前面有多少个人的编号比他大,求这个排列是什么.n<=8000 poj2182题解: 逆序做,可以确定二分最后一个是什么,然后删除这个数.树状 ...

  7. K - Transformation HDU - 4578 线段树经典题(好题)

    题意:区间  加   变成定值 乘  区间查询:和 平方和 立方和 思路:超级超级超级麻烦的一道题  设3个Lazy 标记分别为  change 改变mul乘 add加  优先度change>m ...

  8. 【bzoj3747】[POI2015]Kinoman - 线段树(经典)

    Description 共有m部电影,编号为1~m,第i部电影的好看值为w[i]. 在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部. 你可以选择l,r(1<=l< ...

  9. 2018.10.16 spoj Can you answer these queries V(线段树)

    传送门 线段树经典题. 就是让你求左端点在[l1,r1][l1,r1][l1,r1]之间,右端点在[l2,r2][l2,r2][l2,r2]之间且满足l1≤l2,r1≤r2l1\le l2,r1 \l ...

随机推荐

  1. 集成 SOLR 到 TOMCAT 中(傻瓜教程)

    按照如下配置,整个 Solr 是绿色版的,可以将 Tomcat 目录复制到任何一个地方运行 1.下载 solr 4.3 版本 2.下载 Tomcat 7 ( 6 也可以),另外可以根据系统下载 32 ...

  2. Kotlin——初级篇(四):控制语句详解

    在前面 的章节中讲解了Kotlin语言中的数据类型.变量与常量的定义.不了解请参见前面的内容: Kotlin--初级篇(三):数据类型详解. Kotlin--初级篇(二)常量.变量.注释. 下面详细为 ...

  3. “线程安全的” Dictionary(TKey,TValue)

    这是一篇翻译,专门介绍Dictionary线程安全问题,原文网址如下 http://www.grumpydev.com/2010/02/25/thread-safe-dictionarytkeytva ...

  4. M - Tempter of the Bone(DFS,奇偶剪枝)

    M - Tempter of the Bone Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & % ...

  5. 海康、大华NVR网络硬盘录像机录像无插件全平台访问实现—录像回放时间轴功能实现方法

    在之前的博文中我们有介绍方案*NVR硬件录像机web无插件播放方案(支持取特定时间段视频流)*:该片博文旨在介绍时间轴功能的实现和相关接口的调用: 时间轴样式展示: 问题分析 对于 时间轴的展示实现需 ...

  6. jQuery 文档操作方法(append)

    这些方法对于 XML 文档和 HTML 文档均是适用的,除了:html(). 一.append() 方法 append() 方法在被选元素的结尾(仍然在内部)插入指定内容. 例子: <html& ...

  7. java.sql.SQLException: Incorrect string value: '\xF0\x9F\x98\x87<b

    实际开发中遇到的 情景: 解决方案:  复制的别人的博客,没测试, Incorrect string value: '\xF0\x9F...' for column 'XXX' at row 1 这个 ...

  8. 超出字数部分省略(主要解决不兼容;display: -webkit-box;的浏览器)

    注明:内容于http://www.cnblogs.com/chentongtong/p/5474553.html进一步整理. 1.现webkit内核的浏览器支持display: -webkit-box ...

  9. Python操作Redis(二)

    List操作 redis中的List在在内存中按照一个name对应一个List来存储.如图: lpush(name,values) # 在name对应的list中添加元素,每个新的元素都添加到列表的最 ...

  10. 斯坦福大学Andrew Ng - 机器学习笔记(7) -- 异常检测

    大概用了一个月,Andrew Ng老师的机器学习视频断断续续看完了,以下是个人学习笔记,入门级别,权当总结.笔记难免有遗漏和误解,欢迎讨论. 鸣谢:中国海洋大学黄海广博士提供课程视频和个人笔记,在此深 ...