题意:给出一棵有根树(1为根),边有长度。每个点u有三个属性(len[u],p[u],q[u]),每次u可以转移到u的某个祖先节点v(v满足dist(u,v)<=len[u]),
代价为p[u]*dist(u,v)+q[u]。求每个点都转移到1的代价。
思路:
树分治+cdq+维护凸壳
1.树分治找出重心
2.处理重心到1的路径上的点
3.处理出重心的答案
4.维护len[u]-到重心的距离,并排序
5.维护凸壳,二分答案
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N=2e5+100;
int fa[N],head[N],tot;
long long sv[N],pv[N],qv[N],lv[N],ans[N],len[N];
int Count,size[N],f[N],root;
bool Del[N];
struct Edge{
int to,next;
long long w;
}e[N*2];
void addedge(int from,int to,long long w){
e[tot]=(Edge){to,head[from],w};
head[from]=tot++;
}
void init(){
tot=0;
memset(head,-1,sizeof(head));
}
void getroot(int u,int pre){
f[u]=0,size[u]=1;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(v==pre||Del[v]) continue;
getroot(v,u);
size[u]+=size[v];
f[u]=max(f[u],size[v]);
}
f[u]=max(f[u],Count-size[u]);
if(f[u]<f[root]) root=u;
}
void getsize(int u,int pre){
size[u]=1;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(v==pre||Del[v]) continue;
getsize(v,u);
size[u]+=size[v];
}
}
struct node{
long long Len;
int id;
}a[N];
int TOT;
bool cmp(const node& u,const node& v){
return u.Len<v.Len;
}
void getdeep(int u,int pre,int Belong){
a[++TOT]=(node){lv[u]-(len[u]-len[Belong]),u};
size[u]=1;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(v==pre||Del[v]) continue;
getdeep(v,u,Belong);
size[u]+=size[v];
}
}
int q[N];
double getdet(int x,int y){
return 1.0*(ans[x]-ans[y])/(len[x]-len[y]);
}
int find(int tail,double k){
int low=1,high=tail-1;
while(low<high){
int mid=low+high>>1;
if(getdet(q[mid],q[mid+1])>k) low=mid+1;
else high=mid;
}
if(low==tail-1&&getdet(q[low],q[tail])>k) return q[tail];
return q[low];
}
void work(int u){
Del[u]=true;
int up=u;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(!Del[v]&&v==fa[u]){
while(up!=1&&!Del[fa[up]]) up=fa[up];
getsize(up,0);
Count=size[0]=size[up];
getroot(up,root=0);//找到新的根
work(up);
break;
}
}
//处理u这个节点
int x=u;
while(x!=fa[up]&&x!=up){
if(len[u]-len[fa[x]]>lv[u]) break;
long long tmp=ans[fa[x]]+(len[u]-len[fa[x]])*pv[u]+qv[u];
if(ans[u]==-1) ans[u]=tmp;
else ans[u]=min(ans[u],tmp);
x=fa[x];
}
//处理u之下的结点
TOT=0;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(!Del[v]) getdeep(v,u,u);
}
sort(a+1,a+TOT+1,cmp);
//维护凸壳
int tail=0;
long long LEN=0;
q[++tail]=u,x=u;
for(int i=1;i<=TOT;i++){
if(a[i].Len<0) continue;
while(x!=up&&LEN+sv[x]<=a[i].Len){
while(tail>=2&&getdet(fa[x],q[tail])>getdet(q[tail],q[tail-1]))
tail--;
q[++tail]=fa[x],LEN+=sv[x];
x=fa[x];
}
int xx=find(tail,(double)pv[a[i].id]);
long long tmp=ans[xx]+(len[a[i].id]-len[xx])*pv[a[i].id]+qv[a[i].id];
if(ans[a[i].id]==-1) ans[a[i].id]=tmp;
else ans[a[i].id]=min(ans[a[i].id],tmp);
}
//继续分治
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(!Del[v]){
Count=f[0]=size[v];
getroot(v,root=0);
work(root);
}
}
}
int vis[N];
void bfs(int u){
memset(vis,0,sizeof(vis));
len[u]=0,vis[u]=1;
queue<int>Q;
Q.push(u);
while(!Q.empty()){
u=Q.front();
Q.pop();
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(vis[v]==1) continue;
vis[v]=1,len[v]=len[u]+e[i].w;
Q.push(v);
}
}
}
int main(){
int n,t;
scanf("%d%d",&n,&t);
init();
for(int i=2;i<=n;i++){
scanf("%d%lld%lld%lld%lld",&fa[i],&sv[i],&pv[i],&qv[i],&lv[i]);
addedge(fa[i],i,sv[i]);
addedge(i,fa[i],sv[i]);
}
bfs(1);
memset(ans,-1,sizeof(ans));
ans[1]=0;
Count=f[0]=n;
memset(Del,false,sizeof(Del));
getroot(1,root=0);
work(root);
for(int i=2;i<=n;i++)
printf("%lld\n",ans[i]);
return 0;
}