现在要求你在线动态维护一个二维平面直角坐标系,支持插入一条线段,询问与直线x=x0相交的所有线段中交点y的最大/最小值
–李超线段树解决的问题
现在我们只考虑询问最大,事实上最小是同理的
线段树维护覆盖这个区间的”最优势线段”,”最优势线段”指覆盖此区间且暴露最多的线段
比如对于区间A红色为”最优势线段”
可以证明对于一个位置的询问,答案一定在所有包含这个位置的区间的”最优势线段”里
维护的话,考虑当前加入线段与原”最优势线段”的关系
还没有”最优势线段” -> 直接改
新的把旧的盖住了 -> 直接改
旧的把新的盖住了 -> 直接返回
有交点 -> 比较谁更优势,把不优势的下放到交点所在儿子区间上
询问的话自底向上把每个经过的区间的答案取最大值即可
一条线段会没分配到O(logn)个区间上,每个区间至多下放O(logn)次
修改操作复杂度O(log^2n),查询O(logn)
例题
P4254 [JSOI2008]Blue Mary开公司
#include<cstdio>
#include <iostream>
using namespace std;
const int M=500003;
double b[M*2],k[M*2];int val[M*4],cnt,n;
double d[]={0,1e-1,1e-2,1e-3,1e-4,1e-5,1e-6,1e-7,1e-8,1e-9,1e-10};
double read(){
int x=0,w=1,now=0,f=0;double xs=0;char ch=0;
while(ch<'0'||ch>'9'){if(ch=='-') w=-1; ch=getchar();}
while(ch>='0'&&ch<='9'||(ch=='.')){
if(ch=='.'){f=1;ch=getchar();continue; }
if(f) xs+=(((ch-'0')*d[++now]));
else x=((x<<3)+(x<<1)+ch-'0');ch=getchar();
}
xs+=(double)x;
return xs*w;
}
bool check(int x,int y,int t){
return (b[x]+k[x]*(t-1))>(b[y]+k[y]*(t-1));
}
void insert(int o,int l,int r,int id){
if(l>=r){
if(check(id,val[o],l)) val[o]=id;return ;
}int mid=(l+r)>>1;
if(k[id]>k[val[o]]){
if(check(id,val[o],mid)) insert(o<<1,l,mid,val[o]),val[o]=id;
else insert(o<<1|1,mid+1,r,id);
}
else {
if(check(id,val[o],mid)) insert(o<<1|1,mid+1,r,val[o]),val[o]=id;
else insert(o<<1,l,mid,id);
}
}
double ask(int o,int l,int r,int id){
if(l>=r) return b[id]+k[id]*(id-1);
int mid=(l+r)>>1;
double ans=b[val[o]]+k[val[o]]*(id-1);
if(id>mid) return max(ans,ask(o<<1|1,mid+1,r,id));
else return max(ans,ask(o<<1,l,mid,id));
}
int main(){
scanf("%d",&n);
for(int i=1,d;i<=n;i++){char s[20];
scanf("%s",s);
if(s[0]=='P'){cnt++;
b[cnt]=read(),k[cnt]=read();
insert(1,1,M,cnt);
}
else{
scanf("%d",&d);
printf("%d\n",(int)ask(1,1,M,d)/100);
}
}
}
由于蒟蒻没搞懂。。
暂时只有这一个题。。。。
上面的还是转载的
转自http://blog.csdn.net/flere825/article/details/76283734
有例题BZOJ3165 BZOJ1568 BZOJ3938BZOJ4515