李超线段树是什么?在平面直角坐标系中,它支持插入一条线段(直线),询问(x=x_0)时与它相交的线段中(y)的最大(小)值。
它是如何维护的?抽象的说,就是标记永久化线段树维护区间内从(y=infty)往下看没有被覆盖的长度最大的直线
标记永久化是什么?就是不用(pushdown)啦,不懂可以看我的博客。
如何维护这个东西?考虑处理一个区间
如果该区间无线段,记录当前线段,返回
如果该区间线段两端都大于当前线段,直接返回
如果该区间线段两端都小于当前线段,修改该区间线段为当前线段并返回
如果线段有交点,判断那根线段在上方的区间长,修改长的一段的值(不变则不修改),递归处理短的一段。
这个东西复杂度显然是(O(logn))的,因为每次区间长度减半。
上面那个是插入操作,查询就参照线段树单点查询就行了。
注意插入的是直线,直接在(1-n)中全部插入即可,复杂度(O(nlogn))。
#include<cstdio>
#include<algorithm>
#define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
#define per(i, a, b) for (register int i=(a); i>=(b); --i)
using namespace std;
const int N=50005<<2, n=50000;
int vis[N]; double k[N], b[N]; char s[N];
inline int read()
{
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
}
#define mid (l+r>>1)
#define ls (rt<<1)
#define rs (rt<<1|1)
void modify(int rt, int l, int r, double K, double B)
{
if (!vis[rt]) {vis[rt]=1; k[rt]=K; b[rt]=B; return;}
double l1=l*K+B, r1=r*K+B;
double l2=l*k[rt]+b[rt], r2=r*k[rt]+b[rt];
if (l1<=l2 && r1<=r2) return;
if (l1>l2 && r1>r2) {k[rt]=K; b[rt]=B; return;}
double x=(B-b[rt])/(k[rt]-K);
if (l1>l2)
{
if (x<=mid) modify(ls, l, mid, K, B);
else modify(rs, mid+1, r, k[rt], b[rt]), k[rt]=K, b[rt]=B;
}
else
{
if (x>mid) modify(rs, mid+1, r, K, B);
else modify(ls, l, mid, k[rt], b[rt]), k[rt]=K, b[rt]=B;
}
}
double query(int rt, int l, int r, int x)
{
if (l==r) return k[rt]*x+b[rt];
double res=k[rt]*x+b[rt];
if (x<=mid) res=max(res, query(ls, l, mid, x));
else res=max(res, query(rs, mid+1, r, x));
return res;
}
#undef mid
#undef ls
#undef rs
int main()
{
int Q=read();
while (Q--)
{
scanf("%s", s);
if (s[0]=='P')
{
double K, B; scanf("%lf%lf", &B, &K);
modify(1, 1, n, K, B-K);
}
else
{
int x=read(); double ans=query(1, 1, n, x);
printf("%lld
", (long long)(ans/100));
}
}
return 0;
}
只是把上面的直线变成了线段,需要(O(nlog^2n))的复杂度。
#include<cstdio>
#include<cmath>
#include<algorithm>
#define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
#define per(i, a, b) for (register int i=(a); i>=(b); --i)
using namespace std;
const int N=100005<<2, n=100000;
int vis[N], id[N]; double k[N], b[N], K[N], B[N];
inline int read()
{
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
}
#define mid (l+r>>1)
#define ls (rt<<1)
#define rs (rt<<1|1)
void upd(int rt, double K, double B, int ID){k[rt]=K; b[rt]=B; id[rt]=ID;}
void modify(int rt, int l, int r, double K, double B, int ID)
{
if (!vis[rt]) {vis[rt]=1; upd(rt, K, B, ID); return;}
double l1=k[rt]*l+b[rt], r1=k[rt]*r+b[rt];
double l2=K*l+B, r2=K*r+B;
if (l1>=l2 && r1>=r2) return;
if (l1<l2 && r1<r2) {upd(rt, K, B, ID); return;}
double x=1.0*(B-b[rt])/(k[rt]-K);
if (x<=mid)
{
if (l1<=l2) modify(ls, l, mid, K, B, ID);
else modify(ls, l, mid, k[rt], b[rt], id[rt]), upd(rt, K, B, ID);
}
else
{
if (l1>l2) modify(rs, mid+1, r, K, B, ID);
else modify(rs, mid+1, r, k[rt], b[rt], id[rt]), upd(rt, K, B, ID);
}
}
void Modify(int rt, int l, int r, int L, int R, int ID, double K, double B)
{
if (l>=L && r<=R) {modify(rt, l, r, K, B, ID); return;}
if (L<=mid) Modify(ls, l, mid, L, R, ID, K, B);
if (R>mid) Modify(rs, mid+1, r, L, R, ID, K, B);
}
void Max(int &x, int y, int v)
{
double X=K[x]*v+B[x], Y=K[y]*v+B[y];
if (X<Y || (fabs(X-Y)<1e-7 && x>y)) x=y;
}
int query(int rt, int l, int r, int x)
{
if (l==r) return id[rt];
int res=id[rt];
if (x<=mid) Max(res, query(ls, l, mid, x), x);
else Max(res, query(rs, mid+1, r, x), x);
return res;
}
#undef mid
#undef ls
#undef rs
int main()
{
int Q=read(), ans=0, tot=0;
while (Q--)
{
int opt=read();
if (!opt)
{
int x=(read()+ans-1)%39989+1;
printf("%d
", ans=query(1, 1, n, x));
}
else
{
int x0=(read()+ans-1)%39989+1, y0=(read()+ans-1)%1000000000+1;
int x1=(read()+ans-1)%39989+1, y1=(read()+ans-1)%1000000000+1;
if(x0>x1) swap(x0, x1), swap(y0, y1);
K[++tot]=1.0*(y0-y1)/(x0-x1); B[tot]=y0-x0*K[tot];
Modify(1, 1, n, x0, x1, tot, K[tot], B[tot]);
}
}
return 0;
}
其实就是树剖(+)李超线段树,复杂度(O(nlog^3n))。
代码还没写,占坑待填