NAIPC-2016:YATP(点分治+斜率优化)

https://nanti.jisuanke.com/t/32235

This is Yet Another Tree Problem. You are given a tree, where every node has a penalty and every edge has a weight. The cost of a simple path between any two nodes is the sum of the weights of the edges in the path, plus the product of the penalties of the endpoint nodes. Note that a path can have 0 edges, and the cost of such a path is simply the square of the penalty of the node.

For each node, compute the smallest cost of any path starting at that node. The final answer is the sum of all of these minimum costs.

Input
Each input will consist of a single test case. Note that your program may be run multiple times on different inputs. The first line of input will consist of a single integer n ( 1 ≤ n ≤ 200 , 000 ) n (1 \le n \le 200,000) n(1n200,000), which is the number of nodes. The next line will consist of n space-separated integers p ( 1 ≤ p ≤ 1 , 000 , 000 ) p (1 \le p \le 1,000,000) p(1p1,000,000), which is the penalty of each node, in order. Each of the following n - 1 lines will consist of three space-separated integers i, j and w ( 1 ≤ i ≤ n , 1 ≤ j ≤ n , i ≠ j , 1 ≤ w ≤ 1 , 000 , 000 ) (1 \le i \le n,1 \le j \le n,i \ne j,1 \le w \le 1,000,000) (1in,1jn,i̸=j,1w1,000,000), specifying an edge between nodes i and j with weight w.

Output
Output a single integer, which is the sum of all of the lowest cost paths for each node.

样例输入
5
9 7 1 1 9
3 2 8
5 2 10
4 3 10
2 1 2
样例输出
63
思路:点分治+斜率优化

#include<bits/stdc++.h>
using namespace std;
const int MOD=1e6+3;
const int MAX=2e5+10;
typedef long long ll;
vector<pair<int,int> >e[MAX];
int v[MAX];
int son[MAX],siz[MAX];
int sum,root;
void getsum(int k,int fa)
{
    sum++;
    for(int i=0;i<e[k].size();i++)
    {
        pair<int,int>nex=e[k][i];
        if(nex.first==fa||v[nex.first])continue;
        getsum(nex.first,k);
    }
}
void getroot(int k,int fa)
{
    son[k]=0;
    siz[k]=1;
    for(int i=0;i<e[k].size();i++)
    {
        pair<int,int>nex=e[k][i];
        if(nex.first==fa||v[nex.first])continue;
        getroot(nex.first,k);
        siz[k]+=siz[nex.first];
        son[k]=max(son[k],siz[nex.first]);
    }
    son[k]=max(son[k],sum-son[k]);
    if(son[k]<son[root])root=k;
}
struct lenka{ll ax,dx;}A[MAX],B[MAX];
int cmp(const lenka&a,const lenka&b)
{
    if(a.ax==b.ax)return a.dx<b.dx;
    return a.ax<b.ax;
}
int cnt,p[MAX];
ll a[MAX],d[MAX];
void getdeep(int k,int fa)
{
    A[++cnt]=(lenka){a[k],d[k]};
    p[cnt]=k;
    for(int i=0;i<e[k].size();i++)
    {
        pair<int,int>nex=e[k][i];
        if(nex.first==fa||v[nex.first])continue;
        d[nex.first]=d[k]+nex.second;
        getdeep(nex.first,k);
    }
}
ll ans[MAX];
ll check(int x,int y){return B[x].dx+d[y]+B[x].ax*a[y];}
void cal(int k,int fa)
{
    cnt=0;
    d[k]=0;
    getdeep(k,0);
    sort(A+1,A+cnt+1,cmp);
    int top=1;
    B[top]=A[1];
    for(int i=2;i<=cnt;i++)
    {
        while(top>1&&(A[i].dx-B[top].dx)*(A[i].ax-B[top-1].ax)<=(A[i].dx-B[top-1].dx)*(A[i].ax-B[top].ax))top--;
        B[++top]=A[i];
    }
    for(int i=1;i<=cnt;i++)
    {
        int L=1,R=top;
        while(R-L>1)
        {
            int mid=(L+R)/2;
            int mm=(mid+R)/2;
            if(check(mid,p[i])>check(mm,p[i]))L=mid;
            else R=mm;
        }
        ans[p[i]]=min(ans[p[i]],check(L,p[i]));
        ans[p[i]]=min(ans[p[i]],check(R,p[i]));
    }
}
void solve(int k)
{
    cal(k,0);
    v[k]=1;
    for(int i=0;i<e[k].size();i++)
    {
        pair<int,int>nex=e[k][i];
        if(v[nex.first])continue;
        sum=0;
        getsum(nex.first,0);
        root=0;
        son[root]=sum+1;
        getroot(nex.first,0);
        solve(root);
    }
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++)ans[i]=a[i]*a[i];
    for(int i=1;i<n;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        e[x].push_back({y,z});
        e[y].push_back({x,z});
    }
    sum=n;
    root=0;
    son[root]=sum+1;
    getroot(1,0);
    solve(root);
    ll tot=0;
    for(int i=1;i<=n;i++)tot+=ans[i];
    cout<<tot<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值