F. Drivers Dissatisfaction+最小生成树+lca求树上两点的最大值

题目链接:F. Drivers Dissatisfaction

题意:n个点,m条边,每条边有一个w,代表这条路的不满意度,每一条边我们可以花费c来使不满意读-1;然后问你有s,找到一棵生成树是满意度最小

题解:对于s,我们可以知道花费在c最小的边上价值最优,我们可以先求一颗最小生成树,然后枚举没有用到的边,把连接这两点的最长边去掉判段能否更新最小值

这里求树上两点的最短路经过的最大值,我们可以有lca,或者树链抛分都可以

#include<bits/stdc++.h>
#include<set>
#include<cstdio>
#include<iomanip>
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#define pb push_back
#define ll long long
#define fi first
#define se second
#define PI 3.14159265
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#define eps 1e-7
#define pii pair<int,int>
typedef unsigned long long ull;
const int mod=1e3+5;
const ll inf=0x3f3f3f3f3f3f3f;
const int maxn=2e5+5;
using namespace std;
int n,m,w[maxn],c[maxn],f[maxn],dep[maxn],s,head[maxn],cnt;
bool vis[maxn];
int fa[20][maxn],mx[20][maxn];//mx[i][x]保存x往上走2^i最长边的id
struct data{
    int u,v,w,c,id;
    bool operator<(const data b)
    {
        return w<b.w;
    }
}da[maxn];
struct edge
{
    int to,nxt,w,id;
}e[maxn<<1];
int find(int x)
{
    if(f[x]==x)return x;
     return f[x]=find(f[x]);
}
void link(int x,int y)
{
    f[find(y)]=find(x);
}
void add_edge(int u,int v,int id)
{
    e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;e[cnt].id=da[id].id;e[cnt].w=da[id].w;
    e[++cnt].to=u;e[cnt].nxt=head[v];head[v]=cnt;e[cnt].id=da[id].id;e[cnt].w=da[id].w;
}
void dfs(int v)
{
    for(int i=1;i<20;i++)
    {
        fa[i][v]=fa[i-1][fa[i-1][v]];
        if(w[mx[i-1][fa[i-1][v]]]<w[mx[i-1][v]])mx[i][v]=mx[i-1][v];
        else mx[i][v]=mx[i-1][fa[i-1][v]];
    }
    for(int i=head[v];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to!=fa[0][v])
        {
           dep[to]=dep[v]+1;
           fa[0][to]=v;
           mx[0][to]=e[i].id;
           dfs(to);
        }
    }
}
int lca(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    int t=dep[x]-dep[y],temp=0,ans=0;
    for(int i=0;i<20;i++)
    {
        if(t>>i&1)
        {
            if(w[mx[i][x]]>temp)ans=mx[i][x],temp=w[mx[i][x]];
            x=fa[i][x];
        }
    }
    if(x==y)return ans;
    for(int i=19;i>=0;i--)
    {
        if(fa[i][x]!=fa[i][y])
        {
            if(w[mx[i][x]]>temp)ans=mx[i][x],temp=w[ans];
            if(w[mx[i][y]]>temp)ans=mx[i][y],temp=w[ans];
            x=fa[i][x];
            y=fa[i][y];
        }
    }
    if(w[mx[0][x]]>temp)ans=mx[0][x],temp=w[ans];
        if(w[mx[0][y]]>temp)ans=mx[0][y],temp=w[ans];
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=m;i++)cin>>w[i],da[i].w=w[i];
    for(int i=1;i<=m;i++)cin>>c[i],da[i].c=c[i];
    for(int i=1;i<=m;i++) cin>>da[i].u>>da[i].v,da[i].id=i;
    cin>>s;
    sort(da+1,da+1+m);
    for(int i=0;i<=n;i++)f[i]=i;
    ll ans=0,num=-1;int cn=n-1;
    for(int i=1;i<=m;i++)
    {
        if(find(da[i].v)!=find(da[i].u))
        {
            vis[da[i].id]=true;
            link(da[i].v,da[i].u);
            add_edge(da[i].v,da[i].u,i);
            if(num==-1)num=da[i].id;
            else if(c[num]>da[i].c) num=da[i].id;
            ans+=da[i].w;
            if(--cn==0)break;
        }
    }
    ll sum=ans;
    ans=sum-s/c[num];
    dfs(1);
    int del=0;
     for(int i=1;i<=m;i++)
     {
        if(!vis[da[i].id])
        {
            int id=da[i].id;
            int t=lca(da[i].u,da[i].v);
            if(t==0)continue;
            ll tmp=sum-w[t]+w[id]-s/c[id];
            if(tmp<ans)
            {
                del=t;num=da[i].id;
                ans=tmp;
            }
        }
     }
     if(del)vis[del]=false,vis[num]=true;
     cout<<ans<<endl;
    w[num]-=s/c[num];
    for(int i=1;i<=m;i++)
    {
        if(vis[i])
        {
            cout<<i<<" "<<w[i]<<endl;
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/lhclqslove/p/9306783.html

时间: 2024-07-31 15:42:09

F. Drivers Dissatisfaction+最小生成树+lca求树上两点的最大值的相关文章

Codeforces Round #378 (Div. 2) F - Drivers Dissatisfaction

F - Drivers Dissatisfaction 题目大意:给你n个点,m条边,每个边都有一个权重w,每条边也有一个c表示,消耗c元可以把这条边的权重减1,求最多消耗s元的最小生成树. 思路:因为一条边的权重没有下限所以s元肯定是用在一条边上的. 那么我们先跑一个最小生成树,把这棵最小生成树建出来,然后我们枚举用了 s元之后的边,如果这条边不在树上那么加上这条边之后肯定形成了一个环,最优的方案肯定是删掉这个环中权值最大的边再次变成一棵树. 对于边(u,v)来说,如果把这条边加上,那么删掉的

hdu2586 /// tarjan离线求树上两点的LCA

题目大意: 询问一棵树里 u 到 v 的距离 可由 dis[ u到根 ] + dis[ v到根 ] - 2*dis[ lca(u,v) ] 得到 https://blog.csdn.net/csyzcyj/article/details/10051173 #include <bits/stdc++.h> #define mem(i,j) memset(i,j,sizeof(i)) using namespace std; const int N=40005, Q=205; struct EDG

CF E2 - Daleks&#39; Invasion (medium) (LCA求两点树上路径上的最大边权)

http://codeforces.com/contest/1184/problem/E2 题意:给出一副图,首先求出这幅图的最小生成树 , 然后修改这幅图上不属于最小生成树的边权,使得修改后的图在求一边生成树的时候可以包含被修改的边(注意:修改的边权要最大 )题目规定只有一课生成树 分析: 现在我们需要解决所有非树边的任务(MST保证是惟一的).我们要求对于非树边(u, v),正确答案是u和v之间路径上的最大权值MST.(证明:≤:由MSTs的循环特性可知;≥:如果(u, v)的重量大于这个最

POJ 1986 Distance Queries LCA树上两点的距离

题目来源:POJ 1986 Distance Queries 题意:给你一颗树 q次询问 每次询问你两点之间的距离 思路:对于2点 u v dis(u,v) = dis(root,u) + dis(root,v) - 2*dis(roor,LCA(u,v)) 求最近公共祖先和dis数组 #include <cstdio> #include <cstring> #include <vector> using namespace std; const int maxn =

HDU 4081 Qin Shi Huang&#39;s National Road System 最小生成树+倍增求LCA

原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=4081 Qin Shi Huang's National Road System Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 5428    Accepted Submission(s): 1902 Problem Description

Gym - 101173H Hangar Hurdles (kruskal重构树/最小生成树+LCA)

题目大意:给出一个n*n的矩阵,有一些点是障碍,给出Q组询问,每组询问求两点间能通过的最大正方形宽度. 首先需要求出以每个点(i,j)为中心的最大正方形宽度mxl[i][j],可以用二维前缀和+二分或者BFS求. 然后每相邻的两个点建一条权值为min(mxl[i][j],mxl[i'][j'])的边,求出整个图的最小生成树(注意边权要从大到小排序,实际上求出的是边权的“最大生成树”)或者kruskal重构树,对于每组询问(x1,y1),(x2,y2),答案为最小生成树上两点间路径的最小边权,或者

UVa 11354 Bond 最小生成树+LCA倍增

题目来源:UVa 11354 Bond 题意:n个点m条边的图 q次询问 找到一条从s到t的一条边 使所有边的最大危险系数最小 思路:使最大的危险系数尽量小 答案是最小生成树上的边 然后用LCA倍增法记录s和t到他们最近公共祖先的最大值 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 50010; const int INF =

【LCA求最近公共祖先+vector构图】Distance Queries

Distance Queries 时间限制: 1 Sec  内存限制: 128 MB 题目描述 约翰的奶牛们拒绝跑他的马拉松,因为她们悠闲的生活不能承受他选择的长长的赛道.因此他决心找一条更合理的赛道.此题的输入于第一题相同,紧接着下一行输入一个整数K,以后K行为K个"距离问题".每个距离问题包括两个整数,就是约翰感兴趣的两个农场的编号,请你尽快算出这两地之间的距离. N个点,N-1条边 输入 第1行:两个分开的整数:N和M: 第2..M+1行:每行包括4个分开的内容,F1,F2,L,

求树上任意一点所能到达的最远距离 - 树上dp

A school bought the first computer some time ago(so this computer's id is 1). During the recent years the school bought N-1 new computers. Each new computer was connected to one of settled earlier. Managers of school are anxious about slow functionin