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 ...
随机推荐
- 45、Android事件总线分发库的使用
事件总线分发库EventBus和Otto的简介及对比 什么是事件总线管理: a.将事件放到队列里,用于管理和分发b.保证应用的各个部分之间高效的通信及数据.事件分发c.模块间解耦 Event Bus是 ...
- vue之v-bind:style
<div class="collect" @click="collected=!collected"> <i class="fa f ...
- js 自学,云知梦知识 点理论
一.第1章(1--4) 何为js特效 1.定义事件(触发时机+行为) 2.触发事件(行为发生) 3.事件发生是具有重复性 js是基本对象的语言. 面向对像编程 1.如何获得该对象 2.如何 调用该 ...
- Grafana----Table Panel
新的表面板非常灵活,既支持时间序列的多模式,也支持表.注释和原始JSON数据.它还提供日期格式化和值格式化和着色选项.要查看表板动作和测试不同的配置数据,查看台面板展示在grafana操场. Opti ...
- Pycharm中目前用到的快捷键
1.批量注释:Ctrl+/ 2.缩进\不缩进:Tab\Shift+Tab 3.运行:Ctrl+Shift+F10 4.撤销\反撤销:Ctrl+z\Ctrl+shift+z 5.当光标在代码中间,如何回 ...
- JavaScript函数setInterval()和setTimeout()正确的写法
一.常规写法 1.1 不传参数 function a (x, y) { var i = 0; var b = function(){ console.log((x * y) + (i++)); } r ...
- KVM WEB管理工具webvirtmgr安装和使用
生产环境的KVM宿主机越来越多,需要对宿主机的状态进行调控.这里用webvirtmgr进行管理.图形化的WEB,让人能更方便的查看kvm 宿主机的情况和操作 1 安装支持的软件源 yum -y ins ...
- [置顶] 我的Android进阶之旅------>Android中制作和查看自定义的Debug版本Android签名证书
Android应用开发接入各种SDK时会发现,有很多SDK是需要靠package name和的证书指纹SHA1码来识别的,如百度地图SDK.这样如果使用默认自动生成的debug的话就会给开发调试工作带 ...
- mysq数据库的安装和基本操作
一.数据库的简介 1.什么是数据库? 数据库(database,DB)是指长期存储在计算机内的,有组织,可共享的数据的集合.数据库中的数据按一定的数学模型组织.描述和存储,具有较小的冗余,较高的数据独 ...
- iOS应用生命周期
作为应用程序的委托对象,AppDelegate类在应用生命周期的不同阶段会回调不同的方法.首先,让我们先了解一下iOS 应用的不同状态及它们彼此间的关系,见图1 . 图1 iOS应用状态图 下面简要介 ...