突然传来噩耗,所以期末考试提前一周。。。欲哭无泪。。。
题目大意:有n个英雄一字排开,编号1-n,现在有一些怪兽,一波一波的,每次袭击l-r的英雄,当然我们的英雄总能打败怪兽,并获得相应的经验值,获得的经验值= 英雄当前等级*升级因子e,e每次输入给定,经验值累计到一定程度就会升级,总级别k<=10。现在有m个操作,W操作表示有怪兽来袭,Q操作表示询问l-r经验最高值。
题目分析:线段树。不过这题线段树不是那么容易的,TLE了整个下午。。。首先想到的是线段树成段更新,然后写了一个一交,TLE了。。。成段更新也能TLE。。。然后想了想,一开始的成段更新的策略是如果某个区间的英雄状态都一样,那么就返回,如果最后操作导致每个英雄状态都不一样,那么这个更新策略就跟单点更新没什么区别了。所以换一个思路:我们在每个节点里面记录下当前区间的英雄升级需要的最小经验值和最大级别,很显然,级别越高,每次获得的经验就更多,就更容易升级。这样当我们到达某一个区间发现当前区间的等级最高的英雄都无法升级的话,那么lazy标记一下,返回,这样就能大大节省时间。稍微改一下,再交,结果又是TLE。。。无奈看了一下别人的代码,把第二种思路稍微改变了一下,记录的不是当前区间能升级英雄需要的最小经验数,而是需要的最小升级因子。完了一交又是TLE。。。然后反复改反复交。终于过了。。。不过还是有一些疑问,详情请见代码:
这是AC代码:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MIN(a,b) a>b?b:a
#define MAX(a,b) a>b?a:b
const int N = 10005;
int need[15];
struct node
{
int lazy;
int level;//当前区间最大level
int min;//当前区间离升级最小经验值
int max;//当前区间最大经验
}tree[N<<2];
void build(int num,int s,int e)
{
tree[num].lazy = 0;
tree[num].max = 0;
tree[num].min = need[1];
tree[num].level = 1;
if(s == e)
return;
int mid = (s + e)>>1;
build(num<<1,s,mid);
build(num<<1|1,mid + 1,e);
}
void pushdown(int num)
{
tree[num<<1].lazy += tree[num].lazy;
tree[num<<1|1].lazy += tree[num].lazy;
tree[num<<1].max += tree[num<<1].level * tree[num].lazy;
tree[num<<1|1].max += tree[num<<1|1].level * tree[num].lazy;
tree[num<<1].min -= tree[num].lazy;
tree[num<<1|1].min -= tree[num].lazy;
//tree[num<<1].min -= tree[num<<1].level * tree[num].lazy;
//tree[num<<1|1].min -= tree[num<<1|1].level * tree[num].lazy;
tree[num].lazy = 0;
}
void pushup(int num)
{
tree[num].level = MAX(tree[num<<1].level,tree[num<<1|1].level);
tree[num].max = MAX(tree[num<<1].max,tree[num<<1|1].max);
tree[num].min = MIN(tree[num<<1].min,tree[num<<1|1].min);
}
void insert(int num,int s,int e,int l,int r,int a)
{
if(s == e)
{
tree[num].max += tree[num].level * a;
while(need[tree[num].level] <= tree[num].max)
tree[num].level ++;
tree[num].min = (need[tree[num].level] - tree[num].max)/(tree[num].level) + ((need[tree[num].level] - tree[num].max)%(tree[num].level) != 0);//need[tree[num].level + 1] - tree[num].max;
return;
}
if(s == l && e == r)
{
if(a < tree[num].min)//如果当前区间等级最高的那个人都升不了级的话
{
tree[num].max += tree[num].level * a;
tree[num].min -= a;
tree[num].lazy += a;
}
else//这个区间有人要升了,释放lazy
{
int mid = (s + e)>>1;
pushdown(num);
insert(num<<1,s,mid,s,mid,a);
insert(num<<1|1,mid + 1,e,mid + 1,e,a);
pushup(num);
}
return;
}
if(tree[num].lazy)
{
pushdown(num);
}
int mid = (s + e)>>1;
if(r <= mid)
insert(num<<1,s,mid,l,r,a);
else
{
if(l > mid)
insert(num<<1|1,mid + 1,e,l,r,a);
else
{
insert(num<<1,s,mid,l,mid,a);
insert(num<<1|1,mid + 1,e,mid + 1,r,a);
}
}
pushup(num);
}
int query(int num,int s,int e,int l,int r)
{
if(s == l && r == e)
return tree[num].max;
if(tree[num].lazy)
pushdown(num);
int mid = (s + e)>>1;
if(r <= mid)
return query(num<<1,s,mid,l,r);
else
{
if(l > mid)
return query(num<<1|1,mid + 1,e,l,r);
else
return MAX(query(num<<1,s,mid,l,mid),query(num<<1|1,mid + 1,e,mid + 1,r));//如果这样写代码跑出来2000+Ms
}
//int ret1 = query(num<<1,s,mid,l,mid);
//int ret2 = query(num<<1|1,mid + 1,e,mid + 1,r);
//return MAX(ret1,ret2);//如果用这注释掉的代码交跑出来700Ms内,为什么,和上面一种写法有什么不同么,到底为什么!!
}
int main()
{
int i,t;
int n,k,q;
int cas = 0;
char op[4];
int l,r,e;
scanf("%d",&t);
while(t --)
{
scanf("%d%d%d",&n,&k,&q);
for(i = 1;i < k;i ++)
scanf("%d",&need[i]);
need[k] = 1<<30;
printf("Case %d:\n",++cas);
build(1,1,n);
while(q --)
{
scanf("%s",op);
scanf("%d%d",&l,&r);
if(op[0] == 'W')
{
scanf("%d",&e);
insert(1,1,n,l,r,e);
}
else
{
printf("%d\n",query(1,1,n,l,r));
}
}
//if(t)
putchar(10);
}
return 0;
}
//687MS 808K
这题虽然AC了,但是留下了2个未解之谜
疑惑一:请见第一份AC代码的query函数里面,我加注释的部分,为什么2种写法跑出来时间差了好几倍。。。
疑惑二:
这是TLE的代码:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MIN(a,b) a>b?b:a
#define MAX(a,b) a>b?a:b
const int N = 10005;
int need[15];
int n,k,q;
struct node
{
int lazy;
int level;//当前区间最大level
int min;//当前区间离升级最小经验值
int exp;
int max;//当前区间最大经验
}tree[N<<2];
void build(int num,int s,int e)
{
tree[num].lazy = tree[num].exp = 0;
tree[num].max = 0;
tree[num].min = need[1];
tree[num].level = 1;
if(s == e)
return;
int mid = (s + e)>>1;
build(num<<1,s,mid);
build(num<<1|1,mid + 1,e);
}
void pushdown(int num,int s,int e)
{
tree[num<<1].lazy += tree[num].lazy;
tree[num<<1|1].lazy += tree[num].lazy;
tree[num<<1].max += tree[num<<1].level * tree[num].lazy;
tree[num<<1|1].max += tree[num<<1|1].level * tree[num].lazy;
tree[num<<1].min -= tree[num].lazy;
tree[num<<1|1].min -= tree[num].lazy;
//tree[num<<1].min -= tree[num<<1].level * tree[num].lazy;
//tree[num<<1|1].min -= tree[num<<1|1].level * tree[num].lazy;
tree[num].lazy = 0;
}
void pushup(int num,int s,int e)
{
tree[num].level = MAX(tree[num<<1].level,tree[num<<1|1].level);
tree[num].max = MAX(tree[num<<1].max,tree[num<<1|1].max);
tree[num].min = MIN(tree[num<<1].min,tree[num<<1|1].min);
}
void insert(int num,int s,int e,int l,int r,int a)
{
if(s == e)
{
tree[num].max += tree[num].level * (a + tree[num].lazy);//***如果把这里改成tree[num].max += tree[num].level * a;就能AC。。。***为什么!!
while(need[tree[num].level] <= tree[num].max)
tree[num].level ++;
tree[num].min = (need[tree[num].level] - tree[num].max)/(tree[num].level) + ((need[tree[num].level] - tree[num].max)%(tree[num].level) != 0);//need[tree[num].level + 1] - tree[num].max;
return;
}
if(s == l && e == r)
{
if(a < tree[num].min)//如果当前区间等级最高的那个人都升不了级的话
{
tree[num].max += tree[num].level * a;
tree[num].min -= a;
tree[num].lazy += a;
return;
}
else//这个区间有人要升了,释放lazy
{
int mid = (s + e)>>1;
pushdown(num,s,e);
insert(num<<1,s,mid,s,mid,a);
insert(num<<1|1,mid + 1,e,mid + 1,e,a);
pushup(num,s,e);
}
return;
}
if(tree[num].lazy)
{
pushdown(num,s,e);
}
int mid = (s + e)>>1;
if(r <= mid)
insert(num<<1,s,mid,l,r,a);
else
{
if(l > mid)
insert(num<<1|1,mid + 1,e,l,r,a);
else
{
insert(num<<1,s,mid,l,mid,a);
insert(num<<1|1,mid + 1,e,mid + 1,r,a);
}
}
pushup(num,s,e);
}
int query(int num,int s,int e,int l,int r)
{
if(s == l && r == e)
return tree[num].max;
if(tree[num].lazy)
pushdown(num,s,e);
int mid = (s + e)>>1;
if(r <= mid)
return query(num<<1,s,mid,l,r);
else
{
if(l > mid)
return query(num<<1|1,mid + 1,e,l,r);
else
{
return MAX(query(num<<1,s,mid,l,mid),query(num<<1|1,mid + 1,e,mid + 1,r));
}
}
}
int main()
{
int i,t;
int cas = 0;
char op[4];
int l,r,e;
scanf("%d",&t);
while(t --)
{
scanf("%d%d%d",&n,&k,&q);
for(i = 1;i < k;i ++)
scanf("%d",&need[i]);
need[k] = 1<<30;
printf("Case %d:\n",++cas);
build(1,1,n);
while(q --)
{
scanf("%s",op);
scanf("%d%d",&l,&r);
if(op[0] == 'W')
{
scanf("%d",&e);
insert(1,1,n,l,r,e);
}
else
{
printf("%d\n",query(1,1,n,l,r));
}
}
putchar(10);
}
return 0;
}
请看insert函数里面我加注释的部分,为什么那个会导致超时。。。
求解。。。