3631. Delivery Service(倍增lca+树上差分)

本文介绍了一种通过调整树中边的权重以最小化特定路径集合总长度的算法。该算法适用于有根树结构,利用树上差分和LCA算法统计各边被经过的次数,再按覆盖次数为边分配最优权重。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

EOJ Delivery Service Company handles a massive amount of orders in our nation. As is well known, our nation has n cities, with n−1 bi-directional paths connecting them, forming a tree. The length of the i-th path is wi, meaning that the time we have to spend walking through this path is wi.

Now, you, as the headquarter of EOJ, has somehow learned a magic spell. Each time you cast a spell on two paths, the lengths of these two paths magically swap with each other. You can do this magic as many times as you want (possibly none).

We have q orders waiting to be handled. The i-th order demands sending a package from city si to city ti. We kindly ask you to do your magic before all the deliveries start, such that the total time we have to spend on delivering these packages is minimized.

Input

The first line of the input contains one single integer n (1≤n≤2⋅105).

The next n−1 lines tell about the path information from path 1 to path n−1, the i-th of which contains three space-separated integers ui, vi and wi (1≤ui,vi≤n, ui≠vi, 1≤wi≤1000).

The next line contains one integer q (1≤q≤2⋅105).

The next q lines each contains two space-separated integers si and ti (1≤si,ti≤n,si≠ti).

Output

Output one integer, the minimum total time you can achieve.

Examples

input

5
1 2 2
1 3 3
3 4 3
3 5 4
2
1 5
4 2

output

14

题目大概:

给出一颗树,树的边上有边权。然后q次询问,每次询问 u v的最短距离,在询问前,可以用魔法来换任意两个边,可以使用任意次魔法,问所有询问的最小值是多少。

思路:

可以使用任意次魔法,就是可以交换任意边,那么一定把用的最多次数的边变成最小的,以此类推。

需要在树上标记路径覆盖次数,可以用树上差分+lca来实现。

那么最后贪心的给边赋权值就行了。

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+100;
const int mod=1e9+7;
const int INF=1e9+7;
int deep[maxn];
int w[maxn],p[maxn][30];
int use[maxn];
int n,q;
vector<int>G[maxn];

void dfs(int u,int fa,int d)//记录深度和父亲
{
    deep[u]=d;
    p[u][0]=fa;
    for(int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(v==fa)continue;
        dfs(v,u,d+1);
    }
}
void getp()//计算出p数组
{           //p[i][j]  i节点的第2^j祖先是谁
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i<=n;i++){
            if(p[i][j-1]==-1)continue;
                p[i][j]=p[p[i][j-1]][j-1];
        }
}
int lca(int a,int b)//求出a和b的lca
{
    int i,j;
    if(deep[a]<deep[b])swap(a,b);
    for(i=0;(1<<i)<=deep[a];i++);
    i--;
    for(j=i;j>=0;j--)
    {
        if(deep[a]-(1<<j)>=deep[b])
        a=p[a][j];
    }
    if(a==b)return a;
    for(j=i;j>=0;j--)
    {
        if(p[a][j]!=-1&&p[a][j]!=p[b][j])
        {
            a=p[a][j];
            b=p[b][j];
        }
    }
    return p[a][0];
}

int dfs2(int u,int fa)  //计算出所有边的覆盖次数
{
    for(int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(v==fa)continue;
        dfs2(v,u);
        use[u]+=use[v];
    }
}
bool cmp(int a,int b)
{
    return a>b;
}
int main()
{
    memset(p,-1,sizeof(p));
    scanf("%d",&n);
    int u,v;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&u,&v,&w[i]);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    sort(w+1,w+n);
    dfs(1,1,0);
    getp();
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d",&u,&v);
        int lc=lca(u,v);
        use[lc]-=2;
        use[u]++;
        use[v]++;
    }
    dfs2(1,1);
    sort(use+1,use+1+n,cmp);
    ll sum=0;
    for(int i=1;i<n;i++)
    {
        sum+=use[i]*w[i];
    }
    printf("%lld\n",sum);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值