luogu P4069 [SDOI2016]游戏
强行把李超线段树搬到树上。然后这题我调了两天,最后发现数组开小了。。。
我们发现题目就是把凸包放到树上然后求最值。用树剖把树化为序列,然后直接李超线段树就可以了。
惨痛的教训:一共插入的线段数量是 \(O(nlogn)\) 级别的,数组一定要开大/lb。
代码:
#pragma GCC optimize("Ofast,unroll-loops")
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define int long long
using namespace std;
const int N=1000009,INF=123456789123456789ll;
int n,head[N],cnt,m,Index,Min_[N];
struct Edge
{
int nxt,to,w;
}g[N*2];
struct Dot
{
int dfn,rev,siz,son,dep,top,fa,dis;
#define dfn(x) a[x].dfn
#define rev(x) a[x].rev
#define s(x) a[x].siz
#define son(x) a[x].son
#define t(x) a[x].top
#define f(x) a[x].fa
#define dep(x) a[x].dep
#define dis(x) a[x].dis
}a[N];
struct LC_Tree
{
int K[N],B[N],Min[N*4],cnt,tag[N*4];
void Insert(int a,int b) { K[++cnt]=a,B[cnt]=b; }
int val(int id,int k) { return K[id]*dis(rev(k))+B[id]; }
void Modify(int k,int l,int r,int id)
{
if(l==r)
{
tag[k]=val(id,l)<val(tag[k],l)?id:tag[k];
Min[k]=val(tag[k],l);
return;
}
int mid=l+r>>1;
if(K[id]>K[tag[k]])
{
if(val(id,mid)<val(tag[k],mid))
Modify(k<<1|1,mid+1,r,tag[k]),tag[k]=id;
else
Modify(k<<1,l,mid,id);
}
else
{
if(val(id,mid)<val(tag[k],mid))
Modify(k<<1,l,mid,tag[k]),tag[k]=id;
else
Modify(k<<1|1,mid+1,r,id);
}
Min[k]=min(min(val(tag[k],l),val(tag[k],r)),min(Min[k<<1],Min[k<<1|1]));
}
void Change(int k,int l,int r,int x,int y,int id)
{
if(l>=x&&r<=y)
{
// printf("Modify:%lld %lld %lld %lld\n",k,l,r,id);
Modify(k,l,r,id);
return;
}
int mid=l+r>>1;
if(mid>=x)
Change(k<<1,l,mid,x,y,id);
if(mid<y)
Change(k<<1|1,mid+1,r,x,y,id);
Min[k]=min(min(val(tag[k],l),val(tag[k],r)),min(Min[k<<1],Min[k<<1|1]));
}
int Query(int k,int l,int r,int x,int y)
{
if(l>=x&&r<=y) return Min[k];
int mid=l+r>>1,res=INF;
res=min(val(tag[k],max(l,x)),val(tag[k],min(r,y)));
if(mid>=x)
res=min(res,Query(k<<1,l,mid,x,y));
if(mid<y)
res=min(res,Query(k<<1|1,mid+1,r,x,y));
return res;
}
void build(int k,int l,int r)
{
Min[k]=INF;
if(l==r)
return;
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
}T;
void add(int from,int to,int w)
{
g[++cnt].nxt=head[from];
g[cnt].to=to;
g[cnt].w=w;
head[from]=cnt;
}
void dfs(int x,int fa)
{
s(x)=1;
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa)
continue;
f(v)=x,dep(v)=dep(x)+1,dis(v)=dis(x)+g[i].w;
dfs(v,x);
s(x)+=s(v);
if(s(son(x))<s(v))
son(x)=v;
}
// printf("%lld %lld\n",x,son(x));
}
void DFS(int x,int top)
{
dfn(x)=++Index,rev(Index)=x,t(x)=top;
if(son(x))
DFS(son(x),top);
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==f(x)||v==son(x))
continue;
DFS(v,v);
}
// printf("%lld %lld\n",x,t(x));
}
void init()
{
scanf("%lld %lld",&n,&m);
for (int i=1,x,y,z;i<n;i++)
{
scanf("%lld %lld %lld",&x,&y,&z);
add(x,y,z),add(y,x,z);
}
dfs(1,-1);
DFS(1,1);
}
int lca(int x,int y)
{
while(t(x)!=t(y))
{
if(dep(t(x))<dep(t(y)))
swap(x,y);
x=f(t(x));
}
if(dep(x)>dep(y))
swap(x,y);
return x;
}
int Dis(int x,int y) { return dis(x)+dis(y)-2*dis(lca(x,y)); }
void Modify_Path(int x,int y,int A,int B)
{
int LCA=lca(x,y),S=x;
while(t(x)!=t(LCA))
{
// for (int i=dfn(t(x));i<=dfn(x);i++)
// Min_[i]=min(Min_[i],-A*dis(rev(i))+A*Dis(t(x),S)+B+A*dis(t(x)));
T.Insert(-A,A*Dis(t(x),S)+B+A*dis(t(x)));
T.Change(1,1,n,dfn(t(x)),dfn(x),T.cnt);
x=f(t(x));
}
// for (int i=dfn(LCA);i<=dfn(x);i++)
// Min_[i]=min(Min_[i],-A*dis(rev(i))+A*Dis(LCA,S)+B+A*dis(LCA));
T.Insert(-A,A*Dis(LCA,S)+B+A*dis(LCA));
// printf("%lld %lld\n",dfn(LCA),dfn(x));
T.Change(1,1,n,dfn(LCA),dfn(x),T.cnt);
while(t(y)!=t(LCA))
{
// for (int i=dfn(t(y));i<=dfn(y);i++)
// Min_[i]=min(Min_[i],A*dis(rev(i))+A*Dis(t(y),S)+B-A*dis(t(y)));
T.Insert(A,A*Dis(t(y),S)+B-A*dis(t(y)));
T.Change(1,1,n,dfn(t(y)),dfn(y),T.cnt);
y=f(t(y));
}
// for (int i=dfn(LCA);i<=dfn(y);i++)
// Min_[i]=min(Min_[i],A*dis(rev(i))+A*Dis(LCA,S)+B-A*dis(LCA));
T.Insert(A,A*Dis(LCA,S)+B-A*dis(LCA));
T.Change(1,1,n,dfn(LCA),dfn(y),T.cnt);
}
int Query_Path(int x,int y)
{
int ans=INF;
while(t(x)!=t(y))
{
if(dep(t(x))<dep(t(y)))
swap(x,y);
// for (int i=dfn(t(x));i<=dfn(x);i++)
// ans=min(ans,Min_[i]);
ans=min(ans,T.Query(1,1,n,dfn(t(x)),dfn(x)));
x=f(t(x));
}
if(dep(x)>dep(y))
swap(x,y);
// for (int i=dfn(x);i<=dfn(y);i++)
// ans=min(ans,Min_[i]);
ans=min(ans,T.Query(1,1,n,dfn(x),dfn(y)));
return ans;
}
void work()
{
T.B[0]=123456789123456789ll;
T.build(1,1,n);
for (int i=1;i<=n;i++)
Min_[i]=INF;
// puts("chishi");
while(m--)
{
int opt,A,B,C,D;
scanf("%lld",&opt);
if(opt==1)
scanf("%lld %lld %lld %lld",&A,&B,&C,&D),Modify_Path(A,B,C,D);
else
scanf("%lld %lld",&A,&B),printf("%lld\n",Query_Path(A,B));
}
}
signed main()
{
init();
work();
return 0;
}
由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!