管理者(最小生成树+rmq)

题目大意:

G同学是H市的管理者。H市有n个街区,m条双向道路,每条道路连接两个不同的街区,且没有两条道路连接两个一模一样的街区。保证任意两个街区能相互到达。
       由于H市交通拥堵问题日益严重,G同学必须选择其中若干条道路升级为高速公路。升级后必须保证如果只走高速公路,任意两个街区都能相互到达。当然,每条道路升级为高速公路有不同的费用。同时由于未来的城市发展需要,可能要求某一条道路必须升级。问假如第i条道路一定要升级(1≤i≤m),最小花费是多少。1≤n≤2*10^5,n-1≤m≤2*10^5,每一条边费用<=10^9。

输入样例:

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

输出样例:

9
8
11
8
8
8
9

题解:

看到这道题,第一想法就是假如不强制某一条边一定要修建,那么就是一个简单的最小生成树问题。所以我们先建立一棵最小生成树,显然,假如一条边在这个最小生成树上,那么一定要修这条边的最小花费就是最小生成树的边权和。

那么问题来了,如果一条边不在这个最小生成树上呢?因为一定要加这一条边,所以我们就把这条边加进去,于是会形成一个环,我们的任务就是把环上最长的边删掉。对于这样一个环,它有一个性质:假如我们新加入的边连接u和v,那么环的范围就是u-Lca(u,v)-v,所以我们就可以用rmq来解决求最大值这个问题。

#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
using namespace std;

const int maxl = 20;
int n,m,cur,head[800050],fa[800050][21],dep[800050],maxf[800050][21],vis[800020];
long long ans1,ans[800050];
struct tedge
{
    int nex,to,val,num,fro;
}e[805000];
struct data
{
    int u,v,c,num;
}p[800050];

void Add(int u,int v,int c,int num)
{
    cur++;
    e[cur].to = v;
    e[cur].nex = head[u];
    e[cur].val = c;
    e[cur].num = num;
    e[cur].fro = u;
    head[u] = cur;
}

bool cmp(data a,data b)
{
    return (a.c<b.c||(a.c==b.c&&a.u<b.u));
}

int Find(int x)
{
    if (x==fa[x][0]) return x;
    fa[x][0] = Find(fa[x][0]);
    return fa[x][0];
}

void Make_fa()
{
    for (int i=1; i<=maxl; i++)
      for (int j=1; j<=n; j++)
      fa[j][i] = fa[fa[j][i-1]][i-1];
    for (int i=1; i<=maxl; i++)
      for (int j=1; j<=n; j++)
      maxf[j][i] = max(maxf[j][i-1],maxf[fa[j][i-1]][i-1]);
}

void dfs(int root,int f)
{
    dep[root] = dep[f]+1;
    for (int i=head[root]; i!=-1; i=e[i].nex)
    {
        int v = e[i].to;
        if (v==f) continue;
        if (vis[e[i].num]!=1) continue;
        fa[v][0] = root;
        maxf[v][0] = e[i].val;
        dfs(v,root);
    }
}

int Lca(int a,int b)
{
    int u=a,v=b,cnt=0;
    if (dep[u]<dep[v]) swap(u,v);
    for (int i=maxl; i>=0; i--)
    if (dep[fa[u][i]]>dep[v])
    {
        cnt = max(cnt,maxf[u][i]);
        u = fa[u][i];
    }
    if (dep[u]>dep[v])
    {
        cnt = max(cnt,maxf[u][0]);
        u = fa[u][0];
    }
    if (u==v) return cnt;
    for (int i=maxl; i>=0; i--)
    if (fa[u][i]!=fa[v][i])
    {
        cnt = max(cnt,maxf[u][i]);
        cnt = max(cnt,maxf[v][i]);
        u = fa[u][i];
        v = fa[v][i];
    }
    cnt = max(cnt,maxf[u][0]);
    cnt = max(cnt,maxf[v][0]);
    return cnt;
}

int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=0; i<=n; i++)
    head[i] = -1;
    for (int i=1; i<=m; i++)
    {
        int u,v,c;
        scanf("%d%d%d",&p[i].u,&p[i].v,&p[i].c);
        p[i].num = i;
    }
    sort(p+1,p+1+m,cmp);
    for (int i=1; i<=m; i++)
    {
        Add(p[i].u,p[i].v,p[i].c,p[i].num);
        Add(p[i].v,p[i].u,p[i].c,p[i].num);
    }

    for (int i=1; i<=n; i++)
    fa[i][0] = i;
    for (int i=1; i<=cur; i++)
    {
        int u = Find(e[i].fro), v = Find(e[i].to);
        if (u==v) continue;
        ans1+=(long long)e[i].val;  vis[e[i].num] = 1;
        fa[v][0] = u;  fa[e[i].to][0] = u;
    }
    for (int i=1; i<=cur; i++)
    if (vis[e[i].num]==1) ans[e[i].num] = (long long)ans1;

    fa[1][0] = 0;  maxf[1][0] = 0;
    dfs(1,0);
    Make_fa();

    for (int i=1; i<=cur; i++)
    if (vis[e[i].num]!=1)
    {
        int maxe = 0;
        maxe = Lca(e[i].fro,e[i].to);
        ans[e[i].num] = (long long)ans1-maxe+e[i].val;
        i++;
    }

    for (int i=1; i<=m; i++)
    printf("%I64d\n",ans[i]);
    return 0;
}
时间: 2024-12-09 15:25:51

管理者(最小生成树+rmq)的相关文章

UVA 11354 LCA+最小生成树

点击打开链接 题意:给一个无向图,然后有Q次询问U V,问的是U到V的所有路径中的最小值最大 思路:U到V的路径最小值最大,则这条边肯定是最小生成树上的边,那么我们可以先将所有的最小生成树上的边全都找出来,然后现在是一个树,然后跑一边LCA,对于现在询问的U到V,只要找到它们的最近公共祖先,然后两个点向上找到它,比较路径中的最大值即可  PS:这么暴力找跑得有点慢 #include <vector> #include <stdio.h> #include <string.h&

次最小生成树 模版

次小生成树(转) 转载(http://www.cnblogs.com/z360/p/6875488.html) 所谓次小生成树,顾名思义就是从生成树中取出的第二小的生成树. 我们在前面已经说过最小生成树的概念及代码实现了,所以接下来要说的次小生成树应该比较简单理解了. 求次小生成树的两种方法 1:首先求出最小生成树T,然后枚举最小生成树上的边,计算除了枚举的当前最小生成树的边以外的所有边形成的最小生成树Ti,然后求最小的Ti就是次小生成树.2:首先计算出最小生成树T,然后对最小生成树上任意不相邻

HDU1863 畅通工程---(最小生成树)

畅通工程 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 27972    Accepted Submission(s): 12279 Problem Description 省政府"畅通工程"的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可).经过调查评估,得到的统计表中列出

51Nod1601 完全图的最小生成树计数

传送门 我居然忘写题解啦!(记忆废) 不管怎么说,这题还算是一道好题啊--你觉得敦爷出的题会有水题么 -- 这题比较容易把人误导到Boruvka算法之类的东西上去(我们机房去刚D题的人一开始大多也被误导了),但仔细思考之后是可以发现问题的特殊性质的. 听说很多人是从Kruskal算法想到这道题的做法的?好吧我并不是,那我就写写我的思考过程好了-- 记得算导上有一道思考题,判断一个最小生成树算法的正确性.那个算法是这样的:把当前图的点集随意划分成两半,递归两半后选出连接两个点集的边中权值最小的一条

dutacm.club 1094: 等差区间(RMQ区间最大、最小值,区间GCD)

1094: 等差区间 Time Limit:5000/3000 MS (Java/Others)   Memory Limit:163840/131072 KB (Java/Others)Total Submissions:655   Accepted:54 [Submit][Status][Discuss] Description 已知一个长度为 n 的数组 a[1],a[2],-,a[n],我们进行 q 次询问,每次询问区间 a[l],a[l+1],-,a[r?1],a[r] ,数字从小到大

最小生成树求法 Prim + Kruskal

prim算法的思路 和dijkstra是一样的 每次选取一个最近的点 然后去向新的节点扩张 注意这里的扩张 不再是 以前求最短路时候的到新的节点的最短距离 而是因为要生成一棵树 所以是要连一根最短的连枝 所以关键部分修改一下 dist[u] = min(dist[u], e.cost) --->>e是连接 v 和 u的边 同样地 普同写法O(v^2) 用队列优化后O(E*logV) 1 #include <iostream> 2 #include <stdio.h> 3

“我是谁?”-管理者的角色、职责与工作思路.ppt

http://doc.mbalib.com/view/95b6a675adeaf38a2c028bd4f53f0bf6.html 管理者的三大工作重心: 任务(目标).团队.规划.

UVALive-7303- Aquarium【最小生成树】【连通块】

UVALive - 7303- Aquarium 题目链接:7303 题目大意:给你一个r * c的格子,每个格子有一个 ' \ ' 或者 '/' 的墙,以及打掉墙的费用,问使得所有块联通的最小费用.(看图好理解) 题目思路:就是将他化成一个图,联通的块相当于一个点,两个点之间有一条边,边的权值为墙的费用. 转化为连通块的思路是:每个格子看成两部分,左侧和右侧.以一行来看,假设两个格子A,B.那么B格子的右侧的编号一定和A格子的左侧的编号相同.如图所示 给每个格子的左右侧标上号,然后加入边,边的

RMQ问题再临

RMQ问题再临 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 终于,小Hi和小Ho踏上了回国的旅程.在飞机上,望着采购来的特产——小Hi陷入了沉思:还记得在上上周他们去超市的时候,前前后后挑了那么多的东西,都幸运的没有任何其他人(售货员/其他顾客)来打搅他们的采购过程.但是如果发生了这样的事情,他们的采购又会变得如何呢? 于是小Hi便向小Ho提出了这个问题:假设整个货架上从左到右摆放了N种商品,并且依次标号为1到N,每次小Hi都给出一段区间[L, R],小Ho要做