ZSTU 4241 圣杯战争(线段树+经典)
题意: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 圣杯战争(线段树+经典)的更多相关文章
- csu 1555(线段树经典插队模型-根据逆序数还原序列)
1555: Inversion Sequence Time Limit: 2 Sec Memory Limit: 256 MBSubmit: 469 Solved: 167[Submit][Sta ...
- POJ2155 Matrix二维线段树经典题
题目链接 二维树状数组 #include<iostream> #include<math.h> #include<algorithm> #include<st ...
- poj_2528 Mayor's posters (线段树经典题+离散化方法)
由于题面中给定的wall最大长度为10 000 000:若简单用线段树势必会超时. 而注意到题面中规定了输入海报的个数<=1000:因此不妨离散化,使得线段中叶节点仅含有1000个,那么线段最大 ...
- ZSTU 4241 圣杯战争(ST表+二分)
题目链接 ZSTU 4241 问题转化为有很多区间,现在每次给定一个区间求这个区间和之前所有区间中的某一个的交集的最大长度. 强制在线. 首先我们把所有的区间预处理出来. 然后去重(那些被包含的小区 ...
- POJ 2155 二维线段树 经典的记录所有修改再统一遍历 单点查询
本来是想找一个二维线段树涉及懒惰标记的,一看这个题,区间修改,单点查询,以为是懒惰标记,敲到一半发现这二维线段树就不适合懒惰标记,你更新了某段的某列,但其实其他段的相应列也要打标记,但因为区间不一样, ...
- 【poj2182】【poj2828】树状数组/线段树经典模型:逆序查找-空位插入法
poj2182题意:有一个1~n的排列,现在给定每个人前面有多少个人的编号比他大,求这个排列是什么.n<=8000 poj2182题解: 逆序做,可以确定二分最后一个是什么,然后删除这个数.树状 ...
- K - Transformation HDU - 4578 线段树经典题(好题)
题意:区间 加 变成定值 乘 区间查询:和 平方和 立方和 思路:超级超级超级麻烦的一道题 设3个Lazy 标记分别为 change 改变mul乘 add加 优先度change>m ...
- 【bzoj3747】[POI2015]Kinoman - 线段树(经典)
Description 共有m部电影,编号为1~m,第i部电影的好看值为w[i]. 在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部. 你可以选择l,r(1<=l< ...
- 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 ...
随机推荐
- bsd socket 网络通讯必备工具类
传输数据的时候都要带上包头,包头有简单的又复杂的,简单的只要能指明数据的长度就够了. 这里我写了一个工具类,可以方便地将整型的数据长度转换为长度为 4 的字节数组. 另一方面,可以方便的将长度为 4 ...
- 启发式搜索技术A*
开篇 这篇文章介绍找最短路径的一种算法,它的字我比较喜欢:启发式搜索. 对于入门的好文章不多,而这篇文章就是为初学者而写的,很适合入门的一篇.文章定位:非专业性A*文章,很适合入门. 有图有真相,先给 ...
- 【BZOJ3325】[Scoi2013]密码 Manacher
[BZOJ3325][Scoi2013]密码 Description Fish是一条生活在海里的鱼.有一天他很无聊,就到处去寻宝.他找到了位于海底深处的宫殿,但是一扇带有密码锁的大门却阻止了他的前进. ...
- 《从零开始学Swift》学习笔记(Day 34)——静态属性是怎么回事?
原创文章,欢迎转载.转载请注明:关东升的博客 我先来设计一个类:有一个Account(银行账户)类,假设它有3个属性:amount(账户金额).interestRate(利率)和owner(账户名). ...
- MySQL多实例启动停止
原文地址:http://wolfword.blog.51cto.com/4892126/1241304/ 说明:本实验以MySQL 5.1为例来实验. 1.安装MySQL 5.1 yum instal ...
- api xml database 设计一种数据库
w 问题 0-新增和读取,可以忽略更新和删除: 1-被请求方的xml dom结构多层且不定,且未来可能增删某些键(dom节点),且键值长度最值可能无法确定: 3-请求过程可能出现异常exception ...
- JS添加标签
<script> function show(){ $('.add').unbind(); $('.low ...
- python模块学习(四)
re模块 就其本质而言,正则表达式(或 RE)是一种小型的.高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现.正则表达式模式被编译成一系列的字节码,然后由用 C ...
- P2P-BT对端管理协议(附BT协议1.0)
对端管理 指的是远端peer集合的管理(尽管自身client也能够视为一个peer.但对端管理不包括自身peer) 一个client(client)必须维持与每一个远程peer连接的状态信息,即1V1 ...
- 运行 Tomcat, 在 Intellij IDEA 控制台输出中文乱码的解决方法
打开 Run/Debug Configurations → Tomcat Server → 要运行的 Tomcat → Server 页签,在 VM options 中输入: -Dfile.encod ...