标记永久化线段树裸题,大体思路如下。
如果当前线段完全低于插入线段,则对插入线段进行标记。
如果当前线段完全高于插入线段,则直接放弃。
其余分情况讨论......
首先求出两条直线交点坐标,将长的线段作为当前节点的标
记,短的线段继续下放。
这里有一个小的注意事项,每天增加K,所以插入时 B 应先减去 K
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=60010;
double tagb[4*maxn],tagk[4*maxn],B,K,ans;
bool flag[maxn*4];
double cross(double k1,double b1,double k2,double b2){return (b2-b1)/(1.0*(k1-k2));}
int n,x,T;
void insert(int rt,int l,int r,double K,double B)
{
if(!flag[rt]) tagk[rt]=K,tagb[rt]=B,flag[rt]=1;
else
{
int mid=(l+r)>>1;
double f1=1.0*K*l+B,f2=1.0*tagk[rt]*l+tagb[rt],f3=1.0*K*r+B,f4=1.0*tagk[rt]*r+tagb[rt];
if(f1<=f2&&f3<=f4) return;
if(f1>=f2&&f3>=f4) tagk[rt]=K,tagb[rt]=B;
else
{
double xx=cross(K,B,tagk[rt],tagb[rt]);
if(f1>=f2)
{
if(xx<=mid) insert(rt<<1,l,mid,K,B);
else insert(rt<<1|1,mid+1,r,tagk[rt],tagb[rt]),tagk[rt]=K,tagb[rt]=B;
}
else
{
if(mid<xx) insert(rt<<1|1,mid+1,r,K,B);
else insert(rt<<1,l,mid,tagk[rt],tagb[rt]),tagk[rt]=K,tagb[rt]=B;
}
}
}
}
void query(int rt,int l,int r,int x)
{
if(flag[rt]) ans=max(ans,1.0*tagk[rt]*x+tagb[rt]);
if(l==r) return;
int mid=(l+r)>>1;
if(x<=mid) query(rt<<1,l,mid,x);
else query(rt<<1|1,mid+1,r,x);
}
char op[5];
int main()
{
int n=50000;
scanf("%d",&T);
while(T--)
{
scanf("%s",op);
if(op[0]=='P')
{
scanf("%lf%lf",&B,&K);
insert(1,1,n,K,B-K);
}
else
{
scanf("%d",&x);ans=0;query(1,1,n,x);
printf("%lld\n",(long long)floor(ans/100.0));
}
}
return 0;
}