【李超树】【2019杭州集训12.12】不知道
Description
李超树
- 李超树的运用范围比较小,是一种专门处理区间直线问题的线段树。
- 可以在log的时间内满足单点修改——添加一条直线,并在log的时间内查询——询问x为给定值时在所有直线上的最值。简单来说就是维护一个支持插入查询的凸壳。
- 线段树上每一个节点记录一条直线。考虑插入一条直线,没有直线就直接插在这个节点,否则如果完全比当前直线在这个区间内大就替换掉。
- 如果交点在区间内就把交点一侧的直线下传下去,另一个保留在当前节点。
- 这样子单点插入就是一个log的。
- 如果是区间插入一条线段,那么将这条线段下放到log个区间,每个区间深度log,时间就是log^2的。
Solution
- 考场上想点分治,然后DFS时线段树上动态维护一个凸包,结果发现动态维护凸包我并不会做。。。
- 这题有一万种做法,奈何我数据结构太菜,什么都不会。
- 首先动态维护凸包可以用李超树(我居然才知道有这种东西?!),然后。。。然后还是3个log。。
- 换个处理的方法吧。
- 考虑链剖。
- 一段路径可以分成查询log个重链的前缀,以及最上面的一段区间查询。
1.重链前缀
- 首先离线挂到每个节点上。
- 对于每一个重链做李超树的单点插入和单点查询。
- 每一个点插入一次,一次是log2的。
- 每个询问挂到log个重链,每一次查询log,也是log2的。
2.区间查询
- 我们可以树套树。第一层线段树做链剖的DFS序。每一个节点又有一个线段树维护l,r的信息。
- 但是我们再用李超树时间就没了。
- 但是我们现在每一个查询对于第一层树的区间里面的所有节点都要查询到,只是会有l和r的限制而已,所以为什么不离线呢?
- 考虑干脆对于线段树上每一个点维护一个凸包。
- 只需要在插入之前将所有点排好序就好了。
- 同样,查询也可以排好序,对于每一个节点记录一个指针表示当前的x,因为是递增的,所以就是O(1)在一个节点插入和查询,每一次查询第二层线段树是O(log)的,每一个询问挂在第一层线段树又有log个。排序的复杂度也是第一层一个log,第二层之前排序一个log。
- 这部分的复杂度就是log2的了。
- 好一道码农题。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#define maxn 100005
#define maxt 400005
#define maxm 1000005
#define maxt0 4000005
#define LL long long
using namespace std;
int n,Q,i,j,x,y,q[maxn][2];
LL k[maxn],b[maxn],L[maxn],R[maxn],v,ans[maxn];
int em,e[maxn*2],nx[maxn*2],ls[maxn];
struct que{
LL x; int i;
que(LL _x=0,LL _i=0){x=_x,i=_i;}
};
int cmp(que a,que b){return a.x<b.x;}
vector<que> ask[maxn],ask0[maxt];
void Max(LL &x,LL y){x=(x>y)?x:y;}
void insert(int x,int y){
em++; e[em]=y; nx[em]=ls[x]; ls[x]=em;
em++; e[em]=x; nx[em]=ls[y]; ls[y]=em;
}
int sz[maxn],top[maxn],fa[maxn],gs[maxn];
int tot,dfn[maxn],Idfn[maxn],dep[maxn];
void Getsz(int x,int p){
sz[x]=1;
for(int i=ls[x];i;i=nx[i]) if (e[i]!=p){
Getsz(e[i],x),sz[x]+=sz[e[i]];
if (!gs[x]||sz[e[i]]>sz[gs[x]]) gs[x]=e[i];
}
}
void Gettop(int x,int p,int f){
top[x]=f,fa[x]=p,dfn[x]=++tot,Idfn[tot]=x,dep[x]=dep[p]+1;
if (gs[x]) Gettop(gs[x],x,top[x]);
for(int i=ls[x];i;i=nx[i]) if (e[i]!=p&&e[i]!=gs[x])
Gettop(e[i],x,e[i]);
}
void cover(int x,int l,int r,int ll,int rr,que q){
if (l>rr||r<ll) return;
if (ll<=l&&r<=rr) {
ask0[x].push_back(q);
return;
}
int mid=(l+r)>>1;
cover(x<<1,l,mid,ll,rr,q),cover(x<<1^1,mid+1,r,ll,rr,q);
}
void jump(int x,int y,int num,LL v){
while (top[x]!=top[y]) {
if (dep[top[x]]<dep[top[y]]) swap(x,y);
ask[x].push_back(que(v,num));
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
cover(1,1,n,dfn[x],dfn[y],que(v,num));
}
int t[maxt0],vist[maxt0];
void add(int x,int l,int r,int ll,int rr,int i){
if (l>rr||r<ll) return;
vist[x]=1;
if (ll<=l&&r<=rr){
if (!t[x]) {
t[x]=i;
return;
}
int j=t[x];
if (k[i]==k[j]) {t[x]=(b[i]>b[j])?i:j;return;}
if (k[i]<k[j]) swap(i,j);
if (b[j]-b[i]<(k[i]-k[j])*l) {t[x]=i;return;}
if (b[j]-b[i]>(k[i]-k[j])*r) {t[x]=j;return;}
if (l==r) {t[x]=(k[i]*l+b[i]>k[j]*l+b[j])?i:j;return;}
int mid=(l+r)/2;
if (b[j]-b[i]<=(k[i]-k[j])*mid) {t[x]=i;add(x<<1,l,mid,ll,rr,j);}
else {t[x]=j;add(x<<1^1,mid+1,r,ll,rr,i);}
return;
}
int mid=(l+r)>>1;
add(x<<1,l,mid,ll,rr,i),add(x<<1^1,mid+1,r,ll,rr,i);
}
void find(int x,int l,int r,que q){
if (t[x])
Max(ans[q.i],k[t[x]]*q.x+b[t[x]]);
if (l==r) return;
int mid=(l+r)>>1;
if (q.x<=mid) find(x<<1,l,mid,q); else find(x<<1^1,mid+1,r,q);
}
void clear(int x,int l,int r){
if (!vist[x]) return;
t[x]=vist[x]=0;
if (l==r) return;
int mid=(l+r)>>1;
clear(x<<1,l,mid),clear(x<<1^1,mid+1,r);
}
void Mtr_DFS(int x,int p){
add(1,1,maxm,L[x],R[x],x);
for(int i=0;i<ask[x].size();i++)
find(1,1,maxm,ask[x][i]);
if (gs[x]) Mtr_DFS(gs[x],x);
clear(1,1,maxm);
for(int i=ls[x];i;i=nx[i]) if (e[i]!=p&&e[i]!=gs[x])
Mtr_DFS(e[i],x),clear(1,1,maxm);
}
int cnt,w[maxn],totd[maxt],p[maxn],ti[maxt];
int cmp2(int i,int j){return k[i]<k[j]||k[i]==k[j]&&b[i]>b[j];}
vector<int> d[maxt];
void maketree(int x,int l,int r){
totd[x]=0,ti[x]=1;
if (l==r) return;
int mid=(l+r)>>1;
maketree(x<<1,l,mid),maketree(x<<1^1,mid+1,r);
}
void change(int x,int l,int r,int ll,int rr,int i){
if (w[r]<ll||w[l]>rr) return;
if (ll<=w[l]&&w[r]<=rr){
if (totd[x]&&k[d[x][totd[x]-1]]==k[i]) return;
while (totd[x]>1){
int j=d[x][totd[x]-1],l=d[x][totd[x]-2];
if ((b[i]-b[l])*(k[l]-k[j])<=(b[j]-b[l])*(k[l]-k[i]))
totd[x]--;
else break;
}
if (totd[x]==d[x].size()) d[x].push_back(i),totd[x]++;
else d[x][totd[x]++]=i;
return;
}
if (l==r) return;
int mid=(l+r)>>1;
change(x<<1,l,mid,ll,rr,i),change(x<<1^1,mid+1,r,ll,rr,i);
}
LL mx;
void Get(int x,int l,int r,int i,LL v){
while (ti[x]<totd[x]&&
b[d[x][ti[x]-1]]-b[d[x][ti[x]]]<=v*(k[d[x][ti[x]]]-k[d[x][ti[x]-1]])){
ti[x]++;
}
if (totd[x])
Max(mx,k[d[x][ti[x]-1]]*v+b[d[x][ti[x]-1]]);
if (l==r) return;
int mid=(l+r)>>1;
if (i<=mid) Get(x<<1,l,mid,i,v);
else Get(x<<1^1,mid+1,r,i,v);
}
void Doit_merge(int x,int l,int r){
if (ask0[x].size()){
sort(ask0[x].begin(),ask0[x].end(),cmp);
cnt=ask0[x].size();
for(int i=0;i<ask0[x].size();i++) w[i+1]=ask0[x][i].x;
maketree(1,1,cnt);
for(int i=1;i<=r-l+1;i++) p[i]=Idfn[l+i-1];
sort(p+1,p+1+(r-l+1),cmp2);
for(int i=1;i<=r-l+1;i++) change(1,1,cnt,L[p[i]],R[p[i]],p[i]);
for(int i=1;i<=cnt;i++) mx=0,Get(1,1,cnt,i,w[i]),Max(ans[ask0[x][i-1].i],mx);
}
if (l==r) return;
int mid=(l+r)>>1;
Doit_merge(x<<1,l,mid);
Doit_merge(x<<1^1,mid+1,r);
}
int main(){
scanf("%d%d",&n,&Q);
for(i=1;i<=n;i++) scanf("%lld%lld%lld%lld",&k[i],&b[i],&L[i],&R[i]);
for(i=1;i<n;i++) scanf("%d%d",&x,&y),insert(x,y);
Getsz(1,0);
Gettop(1,0,1);
for(i=1;i<=Q;i++)
scanf("%d%d%lld",&x,&y,&v),jump(x,y,i,v);
Mtr_DFS(1,0),
Doit_merge(1,1,n);
for(i=1;i<=Q;i++) printf("%lld\n",ans[i]);
}