tommymio
2020-02-12 12:48:13
在我的博客食用效果更佳
一种高级数据结构,最经典的应用就是维护一个二维平面直角坐标系,支持动态插入线段,询问与直线
解决这个问题,最暴力的做法就是当
李超线段树是一种特殊的线段树,它的特殊之点在于每个区间只记录在当前区间可能成为最优解的线段,即该线段在当前区间的某个取值上有最优解。可以证明当查询的时候,只需要遍历
维护区间内可能成为最优解的线段,且保证时间复杂度就成了这个算法的核心问题。对于这一问题,我们可以使用斜率和线段在当前区间中点上的取值,进行分类讨论。这里就以求最大值为例进行分析。
若当前区间内没有任何线段,则直接将新线段放入当前区间。
若当前区间
若旧线段在
若旧线段在
若当前区间
若当前区间
如何理解通过两条线段在
设
另外,经过观察,可以发现分类讨论的框架与线段树很相似,可以直接放到线段树上维护,这就是李超线段树。
Luogu P4254/P4097 Blue Mary开公司/Segment
这是江苏2008年的省选题/河北2013年的省选题(省选居然考板子题)
除了一些细节需注意以外,这两题很明显是个李超线段树的裸题,就直接上 P4254 的代码了。
#include<cstdio>
#include<iostream>
#include<algorithm>
const int N=500005;
int tot=0;
char s[15];
int tag[4000005];
double k[100005],b[100005];
inline double calc(int i,int x) {return k[i]*(x-1)+b[i];}
void change(int p,int l,int r,int x) {
if(l==r) {
if(calc(tag[p],l)<calc(x,l)) tag[p]=x;
return;
}
if(!tag[p]) {tag[p]=x;return;}
else {
int mid=l+r>>1;
double y1=calc(tag[p],mid),y2=calc(x,mid);
if(k[tag[p]]<k[x]) {
if(y1<=y2) {change(p*2,l,mid,tag[p]);tag[p]=x;}
else {change(p*2+1,mid+1,r,x);}
}
else if(k[tag[p]]>k[x]) {
if(y1<=y2) {change(p*2+1,mid+1,r,tag[p]);tag[p]=x;}
else {change(p*2,l,mid,x);}
}
else if(b[tag[p]]<b[x]) {tag[p]=x;}
}
}
double query(int p,int l,int r,int x) {
if(l==r) return calc(tag[p],l);
double res=calc(tag[p],x);
int mid=l+r>>1;
if(x<=mid) res=std::max(res,query(p*2,l,mid,x));
else res=std::max(res,query(p*2+1,mid+1,r,x));
return res;
}
int main() {
int n;
std::cin>>n;
for(register int i=1;i<=n;++i) {
std::cin>>s;
if(s[0]=='Q') {
int t;
std::cin>>t;
if(tot==0) printf("0\n");
else printf("%d\n",(int)query(1,1,N,t)/100);
}
else {
double s,p;++tot;
std::cin>>b[tot]>>k[tot];
change(1,1,N,tot);
}
}
return 0;
}
Luogu P4069 [SDOI2016]游戏
题目简述
强行上树差评。
这题挺简单的,树剖剖一剖,将树上问题转化为序列问题,就可以用李超线段树来做了。
但需要注意,李超线段树只能维护k值确定的线段,所以我们要将式子适当变形,然后直接在李超线段树上维护区间最值就可以了。
#include<cstdio>
#include<climits>
#include<cstring>
#include<algorithm>
const long long inf=LLONG_MAX;
int cnt=0,num=0,tot=0;
int tag[800005];
long long minn[800005],b[200005],k[200005],dis[100005];
int h[100005],to[200005],ver[200005],w[200005];
int size[100005],d[100005],fa[100005];
int seg[100005],rev[100005],top[100005],son[100005];
inline int read() {
register int x=0,f=1;register char s=getchar();
while(s>'9'||s<'0') {if(s=='-') f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
return x*f;
}
inline void add(int x,int y,int z) {
to[++cnt]=y;
ver[cnt]=h[x];
w[cnt]=z;
h[x]=cnt;
}
inline long long val(int id,int x) {return k[id]*dis[rev[x]]+b[id];}
void change(int p,int l,int r,int L,int R,int id) {//[L,R]查询区间
int mid=l+r>>1;
if(l==r&&val(id,l)<val(tag[p],l)) {tag[p]=id;minn[p]=val(id,l);return;}
else if(l==r&&val(id,l)>=val(tag[p],l)) return;
else if(L<=l&&R>=r) {
if(val(id,l)>=val(tag[p],l)&&val(id,r)>=val(tag[p],r)) return;
else if(val(id,l)<val(tag[p],l)&&val(id,r)<val(tag[p],r)) tag[p]=id;
else {
if(k[id]<k[tag[p]]) {
if(val(id,mid)<=val(tag[p],mid)) {change(p*2,l,mid,L,R,tag[p]);tag[p]=id;}
else {change(p*2+1,mid+1,r,L,R,id);}
}
else {
if(val(id,mid)<=val(tag[p],mid)) {change(p*2+1,mid+1,r,L,R,tag[p]);tag[p]=id;}
else {change(p*2,l,mid,L,R,id);}
}
}
minn[p]=std::min(minn[p],std::min(val(tag[p],l),val(tag[p],r)));
minn[p]=std::min(minn[p],std::min(minn[p*2],minn[p*2+1]));
return;
}
if(L<=mid) change(p*2,l,mid,L,R,id);
if(R>mid) change(p*2+1,mid+1,r,L,R,id);
minn[p]=std::min(minn[p],std::min(minn[p*2],minn[p*2+1]));
}
long long ask(int p,int l,int r,int L,int R) {
if(L<=l&&R>=r) return minn[p];
long long res=std::min(val(tag[p],std::max(l,L)),val(tag[p],std::min(r,R)));
int mid=l+r>>1;
if(L<=mid) res=std::min(res,ask(p*2,l,mid,L,R));
if(R>mid) res=std::min(res,ask(p*2+1,mid+1,r,L,R));
return res;
}
void dfs1(int x) {
size[x]=1;
for(register int i=h[x];i;i=ver[i]) {
int y=to[i];
if(y==fa[x]) continue;
fa[y]=x;d[y]=d[x]+1;
dis[y]=dis[x]+w[i];
dfs1(y);
if(size[son[x]]<size[y]) son[x]=y;
size[x]+=size[y];
}
}
void dfs2(int x,int t) {
seg[x]=++num;rev[num]=x;top[x]=t;
if(son[x]) dfs2(son[x],t);
for(register int i=h[x];i;i=ver[i]) {
int y=to[i];
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
}
inline int LCA(int x,int y) {
while(top[x]!=top[y]) {d[top[x]]>d[top[y]]? x=fa[top[x]]:y=fa[top[y]];}
return d[x]<d[y]? x:y;
}
inline void modify(int x,int y,int id) {
while(top[x]!=top[y]) {
if(d[top[x]]<d[top[y]]) std::swap(x,y);
change(1,1,num,seg[top[x]],seg[x],id);
x=fa[top[x]];
}
if(d[x]<d[y]) std::swap(x,y);
change(1,1,num,seg[y],seg[x],id);
}
inline long long query(int x,int y) {
long long res=inf;
while(top[x]!=top[y]) {
if(d[top[x]]<d[top[y]]) std::swap(x,y);
res=std::min(res,ask(1,1,num,seg[top[x]],seg[x]));
x=fa[top[x]];
}
if(d[x]>d[y]) std::swap(x,y);
return std::min(res,ask(1,1,num,seg[x],seg[y]));
}
int main() {
//freopen("game3.in","r",stdin);
//freopen("game3.ans","w",stdout);
int n=read(),m=read();
k[0]=0;b[0]=inf;
for(register int i=1;i<n;++i) {
int x=read(),y=read(),z=read();
add(x,y,z);add(y,x,z);
}
d[1]=1;
dfs1(1);
dfs2(1,1);
for(register int i=1;i<=4*num;++i) minn[i]=inf;
for(register int i=1;i<=m;++i) {
int op=read(),s=read(),t=read();
if(op==1) {
long long a=read(),c=read();
int u=LCA(s,t);
k[++tot]=-a;b[tot]=a*dis[s]+c;
modify(s,u,tot);
k[++tot]=a;b[tot]=a*(dis[s]-2*dis[u])+c;
modify(u,t,tot);
}
else {
long long res=query(s,t);
if(res==inf) printf("123456789123456789\n");
else printf("%lld\n",res);
}
}
return 0;
}
P4655 [CEOI2017]Building Bridges
题目简述
有
现在想要建造若干座桥,如果一座桥架在第
在造桥前,所有用不到的柱子都会被拆除,因为他们会干扰造桥进程。第
现在政府想要知道,通过桥梁把第
分析&解答
很明显,如果没有最后一句话,这道题不可做。
但是,规定了不可能在端点外任何地方相交,一定是线性的,很明显想到DP。
设
我们可以令
展开
整理可得:
看上去是不是很像斜率优化DP的样子?但是我不会。
这既然是李超线段树的例题,当然要用李超线段树啦( 路人:我信了你的鬼逻辑,你能够动态维护这一堆乱七八糟的式子我把屏幕吃了)
观察一下可以发现,若
于是就变成了对于一个确定的
很明显的,这是一个类似于
将
#include<cstdio>
#include<cstring>
#include<algorithm>
const long long inf=0x3f3f3f3f3f3f3f3f;
int tag[4000005];
long long h[100005],w[100005];
long long k[100005],b[100005];
long long sum[100005],f[100005];
inline int read() {
register int x=0,f=1;register char s=getchar();
while(s>'9'||s<'0') {if(s=='-') f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
return x*f;
}
inline long long val(int x,int id) {return k[id]*x+b[id];}
void add(int p,int l,int r,int id) {
if(l==r) {
if(val(l,id)<val(l,tag[p])) tag[p]=id;
return;
}
int mid=l+r>>1;
if(k[id]<k[tag[p]]) {
if(val(mid,id)<=val(mid,tag[p])) {add(p*2,l,mid,tag[p]);tag[p]=id;}
else {add(p*2+1,mid+1,r,id);}
}
else if(k[id]>k[tag[p]]) {
if(val(mid,id)<=val(mid,tag[p])) {add(p*2+1,mid+1,r,tag[p]);tag[p]=id;}
else {add(p*2,l,mid,id);}
}
else if(b[id]<b[tag[p]]) {
tag[p]=id;
return;
}
}
long long ask(int p,int l,int r,int x) {
if(l==r) return val(x,tag[p]);
int mid=l+r>>1;
long long res=val(x,tag[p]);
if(x<=mid) return std::min(res,ask(p*2,l,mid,x));
else return std::min(res,ask(p*2+1,mid+1,r,x));
}
int main() {
int n=read();
for(register int i=1;i<=n;++i) h[i]=read();
for(register int i=1;i<=n;++i) w[i]=read();
for(register int i=1;i<=n;++i) sum[i]=sum[i-1]+w[i];
f[1]=0;b[0]=inf;
k[1]=-2*h[1];b[1]=h[1]*h[1]-sum[1]+f[1];
add(1,0,1e6,1);
for(register int i=2;i<=n;++i) {
f[i]=ask(1,0,1e6,h[i])+sum[i-1]+h[i]*h[i];
k[i]=-2*h[i];b[i]=h[i]*h[i]-sum[i]+f[i];
add(1,0,1e6,i);
}
printf("%lld\n",f[n]);
return 0;
}
CF932F Escape Through Leaf
PS:应各位dalao的要求加上了李超线段树合并这一内容(
题面说的很清楚了,这题想让我们求树上每个点
设