李超线段树
李超线段树
李超线段树
发现要维护的问题十分难做,所以我们要引入李超线段树。
我们发现,如果在一个区间内,一条线段的整体在另一条线段上方,那么这条线段一定更优,我们称之为最优线段。但是如果并不是这样,应该如何做呢?
这里给出线段为一个区间内的最优线段的条件:
-
线段的定义域覆盖了整个区间。
-
线段在区间中点的位置最高。
现在我们假设
如果当前区间没有最优线段,或者
而如果
最后是比较难的一种情况:如果两个线段谁也没有被谁完全压住,那么他们一定出现交点。
下面记区间中点为
我们比较两条线段在
我们记
-
交点在
左侧:画个图可以发现 只可能在左子区间成为最优线段,故递归左子区间。 -
交点在
右侧:同理,递归右子区间。 -
交点在
处:看 在 还是 的位置更高,就选择哪一边递归。
我们的基本思想就说完了,下面说一下作者在写这道题的时候遇见的错误:
-
把所有模数都看成
。 -
没有看到交点相同时要选择编号最小的线段。
既然是板子题,下面就放一下代码:
#include<bits/stdc++.h>
#define int long long
#define N 100005
#define mod1 39989
#define mod2 1000000000
#define eps 1e-8
using namespace std;
int cnt,res,tot,rt;
struct line{
double k,b;
}a[N];
struct node{
int ls,rs,id;
}w[N<<2];
bool check(int i,int j,int x){
if(a[i].k*x+a[i].b-a[j].k*x-a[j].b>eps)return 1;
if(a[j].k*x+a[j].b-a[i].k*x-a[i].b>eps)return 0;
return i<j;//函数用于判断哪个线段更优
}
void modify(int &u,int l,int r,int L,int R,int now){
if(l>R||r<L)return;//完全无交
if(!u)u=++cnt;//动态开点
if(l>=L&&r<=R){//完全包含
if(check(now,w[u].id,l)&&check(now,w[u].id,r)){//新的线段更优,直接更新
w[u].id=now;
return;
}
if(check(w[u].id,now,l)&&check(w[u].id,now,r)){//旧的线段更优,直接跳过
return;
}
int mid=l+r>>1;
if(check(now,w[u].id,mid)){
swap(now,w[u].id);//区间内最优线段
}
if(check(now,w[u].id,l)){//用更劣的线段更新子区间
modify(w[u].ls,l,mid,L,R,now);
}
if(check(now,w[u].id,r)){
modify(w[u].rs,mid+1,r,L,R,now);
}
}
else{
int mid=l+r>>1;
modify(w[u].ls,l,mid,L,R,now);//不被完全包含,递归解决
modify(w[u].rs,mid+1,r,L,R,now);
}
}
void solve(int u,int l,int r,int p){
if(!u||r<p||l>p)return;//没有点或者完全无交
if(check(w[u].id,res,p)){//当前答案比之前的答案更优
res=w[u].id;
}
if(l==r)return;
int mid=l+r>>1;
solve(w[u].ls,l,mid,p);//递归解决
solve(w[u].rs,mid+1,r,p);
}
signed main(){
int n,las=0;
cin>>n;
while(n--) {
int op,x_0,y_0,x_1,y_1;
cin>>op;
if(op==0){
int k;
cin>>k;
res=0;
k=(k+las-1)%mod1+1;
solve(1,1,mod1,k);//查询答案
las=res;
cout<<res<<'\n';
}
else{
cin>>x_0>>y_0>>x_1>>y_1;
x_0=(x_0+las-1)%mod1+1;
y_0=(y_0+las-1)%mod2+1;
x_1=(x_1+las-1)%mod1+1;
y_1=(y_1+las-1)%mod2+1;
if(x_0>x_1)swap(x_0,x_1),swap(y_0,y_1);//发现反了要换回来
if(x_0==x_1)a[++tot]={0,max(y_0,y_1)*1.0};
else a[++tot].k=1.0*(y_1-y_0)/(x_1-x_0),a[tot].b=y_0-x_0*a[tot].k;//求斜率和截距
modify(rt,1,mod1,x_0,x_1,tot);//修改
}
}
return 0;
}
Blue Mary 开公司
和上一道题差不多,本质都是维护一堆线段然后求一个
所以我们还是直接使用模板。不过需要注意每个位置的截距要减去斜率才是真正的斜率,作者就因为这个挂了很多发。
既然这么简单,就直接放一下代码:
#include<bits/stdc++.h>
#define int long long
#define N 100005
#define mod1 100000
#define eps 1e-8
using namespace std;
int cnt,res,tot,rt,all;
struct line{
double k,b;
}a[N];
struct node{
int ls,rs,id;
}w[N<<2];
bool check(int i,int j,int x){
if(a[i].k*x+a[i].b-a[j].k*x-a[j].b>eps)return 1;
if(a[j].k*x+a[j].b-a[i].k*x-a[i].b>eps)return 0;
return i<j;
}
void modify(int &u,int l,int r,int L,int R,int now){
if(l>R||r<L)return;
if(!u)u=++cnt;
if(l>=L&&r<=R){
if(check(now,w[u].id,l)&&check(now,w[u].id,r)){
w[u].id=now;
return;
}
if(check(w[u].id,now,l)&&check(w[u].id,now,r)){
return;
}
int mid=l+r>>1;
if(check(now,w[u].id,mid)){
swap(now,w[u].id);
}
if(check(now,w[u].id,l)){
modify(w[u].ls,l,mid,L,R,now);
}
if(check(now,w[u].id,r)){
modify(w[u].rs,mid+1,r,L,R,now);
}
}
else{
int mid=l+r>>1;
modify(w[u].ls,l,mid,L,R,now);
modify(w[u].rs,mid+1,r,L,R,now);
}
}
void solve(int u,int l,int r,int p){
if(!u||r<p||l>p)return;
if(check(w[u].id,res,p)){
res=w[u].id;
}
if(l==r)return;
int mid=l+r>>1;
solve(w[u].ls,l,mid,p);
solve(w[u].rs,mid+1,r,p);
}
signed main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
string op;
double x,y;
cin>>op;
if(op[0]=='P'){
cin>>x>>y;
a[++tot].k=y,a[tot].b=x-y;
modify(rt,1,mod1,1,mod1,tot);
}
else{
all++;
int k;
res=0;
cin>>k;
solve(1,1,mod1,k);
cout<<(int)((a[res].k*k+a[res].b)/100)<<'\n';
}
}
return 0;
}
游戏
我们先设
于是进行套路地拆路径,把路径拆成
我们接下来对每一种路径讨论该路径中点加上的值:
:每个点 加上的值为 ,然后展开为 。
可以发现,把这个式子看作一条线段,斜率为
:每个点 加上的值为 ,然后和上面一样进行展开,展开结果为 。
同理,这是一条斜率为
于是我们套路地使用李超线段树进行维护线段,而拆路径可以让我们想到树链剖分,所以我们使用这两个东西维护即可。
所以这道题就基本转化为了模板题,只不过是维护每个横坐标的线段对应的纵坐标最小值,下面就放一下代码:
#include<bits/stdc++.h>
#define int long long
#define N 100005
#define M 200005
#define inf 123456789123456789
using namespace std;
int h[N],e[M],w[M],ne[M],idx;
void add(int a,int b,int c){
e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;
}
int n,m,dfn[N],id[N],dep[N],top[N];
int fa[N],siz[N],son[N],dis[N],ts;
void dfs1(int u,int f){
if(f!=-1)dep[u]=dep[f]+1;
fa[u]=f;
siz[u]=1;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(j==fa[u])continue;
dis[j]=dis[u]+w[i];
dfs1(j,u);
siz[u]+=siz[j];
if(!son[u]||siz[son[u]]<siz[j])son[u]=j;
}
}
void dfs2(int u,int f){
top[u]=f;
dfn[u]=++ts;
id[ts]=u;
if(son[u])dfs2(son[u],f);
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(j==fa[u]||j==son[u])continue;
dfs2(j,j);
}
}
int get_lca(int a,int b){
while(top[a]!=top[b]){
if(dep[top[a]]<dep[top[b]])swap(a,b);
a=fa[top[a]];
}
return dep[a]<dep[b]?a:b;
}
struct line{
int k,b;
int get_y(int x){
return k*dis[id[x]]+b;
}
};
struct node{
int l,r,mi;
line bes;
}tr[N<<2];
void pushup1(int u){
tr[u].mi=min({tr[u].mi,tr[u<<1].mi,tr[u<<1|1].mi});
}
void pushup2(int u,int l,int r){
tr[u].mi=min({tr[u].mi,tr[u].bes.get_y(l),tr[u].bes.get_y(r)});
}
void build(int u,int l,int r){
tr[u]={l,r,inf,{0,inf}};
if(l==r)return;
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup1(u);
}
void modify(int u,int L,int R,line now){
int l=tr[u].l,r=tr[u].r;
if(l>=L&&r<=R){
int lp=tr[u].bes.get_y(l);
int rp=tr[u].bes.get_y(r);
int lq=now.get_y(l);
int rq=now.get_y(r);
if(lp>=lq&&rp>=rq){
tr[u].bes=now;
pushup2(u,l,r);
}
else if(lp>=lq||rp>=rq){
int mid=l+r>>1;
int mp=tr[u].bes.get_y(mid);
int mq=now.get_y(mid);
if(mp>mq)swap(tr[u].bes,now),swap(lp,lq);
if(lp>lq)modify(u<<1,L,R,now);
else modify(u<<1|1,L,R,now);
pushup2(u,l,r);
pushup1(u);
}
}
else{
int mid=l+r>>1;
if(L<=mid)modify(u<<1,L,R,now);
if(R>mid)modify(u<<1|1,L,R,now);
pushup1(u);
}
}
int qry(int u,int L,int R){
int l=tr[u].l,r=tr[u].r;
if(l>=L&&r<=R)return tr[u].mi;
int lx=max(l,L),rn=min(r,R);
int res=min(tr[u].bes.get_y(lx),tr[u].bes.get_y(rn));
int mid=l+r>>1;
if(L<=mid)res=min(res,qry(u<<1,L,R));
if(R>mid)res=min(res,qry(u<<1|1,L,R));
return res;
}
void modify_path(int a,int b,line now){
while(top[a]!=top[b]){
modify(1,dfn[top[a]],dfn[a],now);
a=fa[top[a]];
}
modify(1,dfn[b],dfn[a],now);
}
int qry_path(int a,int b){
int res=inf;
while(top[a]!=top[b]){
res=min(res,qry(1,dfn[top[a]],dfn[a]));
a=fa[top[a]];
}
res=min(res,qry(1,dfn[b],dfn[a]));
return res;
}
signed main(){
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=1;i<n;i++){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);add(b,a,c);
}
dfs1(1,-1);
dfs2(1,1);
build(1,1,n);
while(m--){
int op,s,t,a,b;
cin>>op>>s>>t;
int lca=get_lca(s,t);
if(op==1){
cin>>a>>b;
modify_path(s,lca,{-a,a*dis[s]+b});
modify_path(t,lca,{a,a*(dis[s]-2*dis[lca])+b});
}
else{
int res1=qry_path(s,lca);
int res2=qry_path(t,lca);
cout<<min(res1,res2)<<'\n';
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步