【Codeforces 1303G】Sum of Prefix Sums

点分治,李超树。

题目大意:

给定一棵树,点带点权。要求找到一条有向路径,对路径上的点权依次排列,并进行前缀和,然后求和。要使得最后的和最大。求这个最大的和。

题解:

考虑点分治。

对于点 $x$ 作为分治中心,一条路径的信息记为 $(c,s)$,表示共经过 $c$ 个点,其前缀和的和为 $s$。那么,对于路径 $u\rightarrow x$ 和 $(x)\rightarrow v$($(x)$ 表示不包含 $x$ 这个点),其信息为 $(c_1,s_1)$ 和 $(c_2,s_2)$,其合并后的路径的前缀和的和,可以表示为 $s_1\cdot (c_2+1)+s_2$。

我们发现,对于一组 $(c_2,s_2)$,它的值由 $s_1$ 决定,并且是个一次函数。

我们希望对于每个 $s_1$ 求出最大值。这个最大值显然会出现在这些一次函数围成的凸包上。

使用动态开点李超树来维护凸包即可。

涉及的操作是全局修改,区间查询,所以单层的时间复杂度为 $O(n\log n)$。

总时间复杂度 $O(n\log^2 n)$。

比赛的时候点分治写挂了……

Code:

#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#include<bits/stdc++.h>
using namespace std;
const int N=150005;
typedef long long LL;
LL ans;
const LL INF=1e14;
int n,head[N],cnt,mx[N],sz[N],allsz,rt,w[N];
bool vis[N];
struct edge{
    int to,nxt;
}e[N<<1];
int root;
namespace SGT{
    struct _1{
        LL k,b;
        inline LL operator()(LL x)const{return k*x+b;}
    }A[N*120];
    int ls[N*120],rs[N*120],nod;
    void insert(int&o,LL l,LL r,const LL&k,const LL&b){
        if(!o){
            o=++nod;
            ls[o]=rs[o]=0;
            A[o]=(_1){k,b};
            return;
        }
        const LL yL=A[o](l),yR=A[o](r),nL=k*l+b,nR=k*r+b;
        if(yL>=nL&&yR>=nR)return;
        if(yL<=nL&&yR<=nR){
            A[o]=(_1){k,b};
            return;
        }
        if(l==r)return;
        const LL mid=l+r>>1;
        insert(ls[o],l,mid,k,b);
        insert(rs[o],mid+1,r,k,b);
    }
    LL query(int o,LL l,LL r,const LL&x){
        if(!o)return 0;
        if(l==r)return A[o](x);
        const LL mid=l+r>>1;
        if(x<=mid)
        return max(A[o](x),query(ls[o],l,mid,x));
        return max(A[o](x),query(rs[o],mid+1,r,x));
    }
}
void getroot(int now,int pre){
    sz[now]=1,mx[now]=0;
    for(int i=head[now];i;i=e[i].nxt)
    if(e[i].to!=pre&&!vis[e[i].to]){
        getroot(e[i].to,now);
        sz[now]+=sz[e[i].to];
        mx[now]=max(mx[now],sz[e[i].to]);
    }
    mx[now]=max(mx[now],allsz-sz[now]);
    if(!rt||mx[now]<mx[rt])rt=now;
}
pair<LL,int>down[N];
int top;
LL up[N],ddn[N];
void dfs(int now,int pre,int dep,LL u,LL d,LL sm){
    up[++top]=u,down[top]=make_pair(d,dep);ddn[top]=sm;
    for(int i=head[now];i;i=e[i].nxt)if(e[i].to!=pre&&!vis[e[i].to]){
        dfs(e[i].to,now,dep+1,u+(dep+1)*(LL)w[e[i].to],d+sm+w[e[i].to],sm+w[e[i].to]);
    }
}
void doit(int now){
    SGT::nod=root=0;
    vector<int>vc;
    for(int i=head[now];i;i=e[i].nxt)if(!vis[e[i].to]){
        vc.push_back(e[i].to);
        top=0;
        dfs(e[i].to,0,1,w[e[i].to],w[e[i].to],w[e[i].to]);
        for(int j=1;j<=top;++j){
            LL zb=up[j]+ddn[j]+w[now];
            LL s=ddn[j]+w[now];
            LL mxx=SGT::query(root,0,INF,s);
            ans=max(ans,mxx+zb);
        }
        for(int j=1;j<=top;++j){
            SGT::insert(root,0,INF,down[j].second,down[j].first);
        }
    }
    SGT::nod=root=0;
    for(int i=(int)vc.size()-1;~i;--i){
        int to=vc[i];
        top=0;
        dfs(to,0,1,w[to],w[to],w[to]);
        for(int j=1;j<=top;++j){
            LL zb=up[j]+ddn[j]+w[now];
            LL s=ddn[j]+w[now];
            LL mxx=SGT::query(root,0,INF,s);
            ans=max(ans,mxx+zb);
        }
        for(int j=1;j<=top;++j){
            SGT::insert(root,0,INF,down[j].second,down[j].first);
        }
    }
}
void solve(int now){
    vis[now]=1;
    doit(now);
    int sm=allsz;
    for(int i=head[now];i;i=e[i].nxt)
    if(!vis[e[i].to]){
        allsz=sz[e[i].to]>sz[now]?sm-sz[now]:sz[e[i].to];
        rt=0;
        getroot(e[i].to,now);
        solve(rt);
    }
}
int main() {
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<n;++i){
        int u,v;
        cin>>u>>v;
        e[++cnt]=(edge){v,head[u]},head[u]=cnt;
        e[++cnt]=(edge){u,head[v]},head[v]=cnt;
    }
    for(int i=1;i<=n;++i)cin>>w[i];
    allsz=n,rt=0;
    getroot(1,0);
    solve(rt);
    cout<<ans<<'\n';
    return 0;
}