http://www.lydsy.com/JudgeOnline/problem.php?id=1568 (题目链接)
题意
在线维护若干条直线,询问一些横坐标上的最高直线的纵坐标。
Solution
超哥线段树= =。可以说是标记可持久化的一个好的应用吧。
线段树上一个节点为一个容器,可以存放一条直线。考虑插入一条直线时,当找到要插入的区间节点时,如果这个节点的容器没东西则将这条直线直接放在这个节点的容器中。否则和容器中的线段比较,保留较高部分较多的直线,将另一条直线作为新的插入直线往左儿子或者右儿子递归处理,最后到叶子节点直接比较即可。
这样一次插入,在线段树上最多覆盖满$log$个区间,每个区间递归下去$log$层。查询时间复杂度为一个$log$。时间复杂度为$O(nlog^2n)$
细节
记得各种eps。
代码
// bzoj1568 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #define LL long long #define inf (1ll<<60) #define eps 1e-8 #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout) using namespace std; const int lim=50000,maxn=100010; int n; char ch[100]; struct seg { double k,b; double ch(int x) {return k*x+b;} }t; struct node {int cov;seg mx;}tr[maxn<<2]; double X(seg a,seg b) { return fabs(a.k-b.k)<eps ? inf : (a.b-b.b)/(b.k-a.k); } void insert(int k,int l,int r,seg g) { int mid=(l+r)>>1; if (!tr[k].cov) tr[k].cov=1,tr[k].mx=g; else { seg a1=g,a2=tr[k].mx;double x=X(a1,a2); if (a1.ch(l)<a2.ch(l) || (fabs(a1.ch(l)-a2.ch(l))<eps && a1.k<a2.k)) swap(a1,a2); if (x<=l || x>=r) {tr[k].mx=a1;return;} if (x<=mid) tr[k].mx=a2,insert(k<<1,l,mid,a1); else tr[k].mx=a1,insert(k<<1|1,mid+1,r,a2); } } double query(int k,int l,int r,int x) { if (l==r) return tr[k].cov ? tr[k].mx.ch(x) : 0; int mid=(l+r)>>1;double t; if (x<=mid) t=query(k<<1,l,mid,x); else t=query(k<<1|1,mid+1,r,x); return tr[k].cov ? max(t,tr[k].mx.ch(x)) : t; } int main() { scanf("%d",&n); for (int x,i=1;i<=n;i++) { scanf("%s",ch); if (ch[0]=='P') scanf("%lf%lf",&t.b,&t.k),insert(1,0,lim,t); if (ch[0]=='Q') scanf("%d",&x),printf("%lld ",(LL)query(1,0,lim,x-1)/100); } return 0; }