BZOJ 1568 [JSOI2008]Blue Mary开公司
线段树
题意
插入n条直线 y=kx+b ,然后查询某x位置所有直线中的最大值。
思路
线段树,每个区间维护:在这个区间内,值最大的线段的k与b。有个性质:因为所有斜率非负,所以斜率是不减的。就是越往后的区间,斜率不会小。
每次加入一条直线,先判断直线的斜率和本区间记录的直线的斜率,再判断区间中点哪条直线的答案大,如果是斜率大的直线答案大,因为斜率递增,所以在[mid+1,r]中斜率小的直线不会再产生贡献,那么我们把当前区间的答案更改为斜率大的直线,同时将斜率小的直线向[l,mid]中下方。
如果是斜率小的直线的答案大,那么斜率大的直线只可能在[mid+1,r]中产生贡献,那么就把当前区间的答案更改为斜率较小的直线,并把斜率大的直线向[mid+1,r]下方。
查询答案的时候就把经过的区间所记录的直线该点的值都计算一下,取最大值就是答案。
标记永久化,带待我看懂再说。%%%
代码
我根据自己的理解,优化了一下边界,跑的可能更快了一点。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define M(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MAXN=50007;
struct Stree
{
double k;double b;
}stree[MAXN<<2];
void build()
{
M(stree, 0);
}
void update(double K, double B, int l, int r, int rt)
{
if(l==r) return;
double l1=(double)K*l+B, r1=(double)K*r+B;
double l2=(double)stree[rt].k*l+stree[rt].b,
r2=(double)stree[rt].k*r+stree[rt].b;
double x=(B-stree[rt].b)/(stree[rt].k-K);
int mid=(l+r)>>1;
if((l1>=l2&&r1>r2)||(l1>l2&&r1>=r2))
stree[rt].k=K, stree[rt].b=B;
else if(l1<=l2&&r1<=r2) return;
else
{
if(x<=mid)
{
if(l1>l2)
update(K, B, lson);
else
{
update(stree[rt].k, stree[rt].b, lson);
stree[rt].k=K, stree[rt].b=B;
}
}
else
{
if(l1>l2)
{
update(stree[rt].k, stree[rt].b, rson);
stree[rt].k=K, stree[rt].b=B;
}
else
update(K, B, rson);
}
}
}
double query(int pos, int l, int r, int rt)
{
double k=stree[rt].k, b=stree[rt].b;
double res=k*pos+b;
if(l==r) return res;
int mid=(l+r)>>1;
if(pos<=mid)
res=max(res, query(pos, lson));
else
res=max(res, query(pos, rson));
return res;
}
int main()
{/*
freopen("in.txt", "r", stdin);
freopen("ou1.txt", "w", stdout);*/
build();
int n;scanf("%d", &n);
char s[10];double k, b;int pos;
while(n--)
{
scanf("%s", s);
if(s[0]=='P')
{
scanf("%lf%lf", &b, &k);
update(k, b-k, 1, MAXN, 1);
}
else
{
scanf("%d", &pos);
double res=query(pos, 1, MAXN, 1);
double tmp=res/100.0;
int R=tmp;
printf("%d\n", R);
}
}
return 0;
}