bzoj1977

1977: [BeiJing2010组队]次小生成树 Tree

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 3001  Solved: 751
[Submit][Status][Discuss]

Description


C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C
冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是
EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值) 这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。

Input

第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。

Output

包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)

Sample Input

5 6

1 2 1

1 3 2

2 4 3

3 5 4

3 4 3

4 5 6

Sample Output

11

HINT

数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。

Source

首先我们要知道次小生成树是最小生成树删掉一条边并加上一条边,那么枚举每条边,通过倍增计算最小边和次小边,计算答案即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
#define N 100010
struct edge
{
    int to,nxt,w;
}e[N*6];
struct edge1
{
    int u,v,w;
};
vector<edge1> E;
int n,m,cnt=1;
ll tot;
int head[N],father[N],dep[N],mark[N];
int fa[N][23];
ll maxcost[N][23];
ll max(ll x,ll y)
{
    return x>y?x:y;
}
ll min(ll x,ll y)
{
    return x<y?x:y;
}
bool cp(edge1 x,edge1 y)
{
    return x.w<y.w;
}
void link(int u,int v,int w)
{
    e[++cnt].nxt=head[u];
    head[u]=cnt;
    e[cnt].to=v;
    e[cnt].w=w;
}
int find(int u)
{
    return u==father[u]?father[u]:find(father[u]);
}
void connect(int u,int v)
{
    int a=find(u);
    int b=find(v);
    if(a==b) return;
    father[a]=b;
}
void dfs(int u,int Fa)
{
    for(int i=head[u];i;i=e[i].nxt) if(e[i].to!=Fa)
    {
        int v=e[i].to;
        dep[v]=dep[u]+1;
        fa[v][0]=u;
        maxcost[v][0]=e[i].w;
        dfs(v,u);
    }
}
PII lca(int u,int v)
{
    PII ret;
    if(dep[u]<dep[v]) swap(u,v);
    for(int i=22;i>=0;i--) if((dep[u]-dep[v])&(1<<i))
    {
        if(maxcost[u][i]>ret.first)
        {
            ret.second=ret.first;
            ret.first=maxcost[u][i];
        }
        else if(maxcost[u][i]<ret.first)
            ret.second=max(ret.second,maxcost[u][i]);
        u=fa[u][i];
    }
    if(u==v) return ret;
    for(int i=22;i>=0;i--) if(fa[u][i]!=fa[v][i])
    {
        if(maxcost[u][i]>ret.first)
        {
            ret.second=ret.first;
            ret.first=maxcost[u][i];
        }
        else if(maxcost[u][i]<ret.first)
            ret.second=max(ret.second,maxcost[u][i]);
        if(maxcost[v][i]>ret.first)
        {
            ret.second=ret.first;
            ret.first=maxcost[v][i];
        }
        else if(maxcost[v][i]<ret.first)
            ret.second=max(ret.second,maxcost[v][i]);
        u=fa[u][i]; v=fa[v][i];
    }
    if(maxcost[u][0]>ret.first)
    {
        ret.second=ret.first;
        ret.first=maxcost[u][0];
    }
    else if(maxcost[u][0]<ret.first)
        ret.second=max(ret.second,maxcost[u][0]);
    if(maxcost[v][0]>ret.first)
    {
        ret.second=ret.first;
        ret.first=maxcost[v][0];
    }
    else if(maxcost[v][0]<ret.first)
        ret.second=max(ret.second,maxcost[v][0]);
    return ret;
}
void build()
{
    for(int i=1;i<=22;i++)
        for(int j=1;j<=n;j++) if(fa[j][i-1]!=-1)
        {
            fa[j][i]=fa[fa[j][i-1]][i-1];
            maxcost[j][i]=max(maxcost[j][i-1],
            maxcost[fa[j][i-1]][i-1]);
        }
}
void kruskal()
{
    sort(E.begin(),E.end(),cp);
    for(int i=1;i<=n;i++) father[i]=i;
    for(int i=0;i<E.size();i++) if(find(E[i].u)!=find(E[i].v))
    {
        connect(E[i].u,E[i].v);
        mark[i]=1;
        tot+=E[i].w;
        link(E[i].u,E[i].v,E[i].w);
        link(E[i].v,E[i].u,E[i].w);
    }
    dfs(1,1);
    build();
    ll ans=(ll)(1e17);
    for(int i=0;i<E.size();i++) if(!mark[i])
    {
        PII x=lca(E[i].u,E[i].v);
        if(x.first<E[i].w) ans=min(ans,tot+E[i].w-x.first);
        else if(x.first==E[i].w) ans=min(ans,tot+E[i].w-x.second);
    }
    printf("%lld",ans);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v,w; scanf("%d%d%d",&u,&v,&w);
        edge1 x;
        x.u=u; x.v=v; x.w=w;
        E.push_back(x);
    }
    kruskal();
    return 0;
}
时间: 2024-10-03 21:53:53

bzoj1977的相关文章

BZOJ1977 [BeiJing2010组队]次小生成树 Tree

恩,归类上来讲的话...是一道非常好的noip题...只不过嘛...(此处省略100字) 然后将如何做: 首先Kruskal求出最小生成树. 我们其实可以发现严格的次小生成树不可能在MST上改两条边 <=> 只能改一条边. 那么如何改呢? 每次在MST中加入一条非树边,即不在MST的边,那么会形成一个环,只要找到换上的严格小于当前边权的最大值,删之,就形成了次小生成树的候选. 由Kruskal的算法保证加入的边权一定是环上最大的,因此我们要记录树链上的最大值和次大值(因为是严格小于) 而记录的

严格次小生成树(Bzoj1977:[Beijing2010组队]次小生成树)

非严格次小生成树 很简单,先做最小生成树 然后枚举没加入的边加入,替换掉这个环内最大的边 最后取\(min\) 严格次小生成树 还是一样的 可以考虑维护一个严格次大值 最大值和枚举的边相同就替换次大值的边 否则替换最大值的边 最后取\(min\) 裸题 Luogu 随你用各种姿势\(AC\) \(LCT\)常数大,但是好写,开\(O2\)可以过 # include <bits/stdc++.h> # define RG register # define IL inline # define

bzoj1977 [BeiJing2010组队]次小生成树 Tree——严格次小生成树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1977 因为严格,所以要记录到 LCA 的一个次小值: 很快写好,然后改掉一堆错误后终于过了样例!然而交上去1WA: 又改了半天,还是WA,于是放弃,抄题解好久... 然而就在我调了一个小时终于锁定错误就在那个子函数里的时候才突然看到了自己的明显惊天大错误是怎么回事??!!!稍微改了一下下就完美AC... 不过还有点收获,把求各种层次的 f 放在 dfs 函数里会比单独拿出来再求一遍快 10

bzoj1977次小生成树(重要)

#include<cstdio> #include<iostream> #include<cstring> #include<queue> #include<algorithm> #define Maxn 300010 #define maxn 300005 using namespace std; #define ll long long struct edge{ int to,w,nxt; }edge[Maxn]; int head[Maxn

[bzoj1977]次小生成树

先求出最小生成树,然后维护f1/f2[i][j]表示i到$2^{j}-1$祖先中最大和严格次大边,枚举生成树外的每一条边并查询这条边两点间的最大边和严格次大边,若最大边<插入边,就用插入边替换最大边计算答案,否则用插入边替换次大边计算答案 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 100005 4 #define oo 0x3f3f3f3f 5 struct ji{ 6 int x,y; 7 }o,dp[N]

大神刷题表

9月27日 后缀数组:[wikioi3160]最长公共子串 dp:NOIP2001统计单词个数 后缀自动机:[spoj1812]Longest Common Substring II [wikioi3160]最长公共子串 [spoj7258]Lexicographical Substring Search 扫描线+set:[poj2932]Coneology 扫描线+set+树上删边游戏:[FJOI2013]圆形游戏 结论:[bzoj3706][FJ2014集训]反色刷 最小环:[poj1734

[转载]hzwer的bzoj题单

counter: 664BZOJ1601 BZOJ1003 BZOJ1002 BZOJ1192 BZOJ1303 BZOJ1270 BZOJ3039 BZOJ1191 BZOJ1059 BZOJ1202 BZOJ1051 BZOJ1001 BZOJ1588 BZOJ1208 BZOJ1491 BZOJ1084 BZOJ1295 BZOJ3109 BZOJ1085 BZOJ1041 BZOJ1087 BZOJ3038 BZOJ1821 BZOJ1076 BZOJ2321 BZOJ1934 BZOJ

次小生成树(LCA倍增)

算法: 求出MST之后枚举每条在MST之外的边 连上之后会出现环 找到环中除加上的边之外权值最大的边 删除该边之后得到一颗新树 做法: 利用LCA倍增地维护最小生成树上两点之间的最大边权 每次枚举在MST之外的边 有两种情况 ①.两个端点在一条链上 ②.两个端点不在一条链上 第一种情况就直接得到答案 第二种情况的话分两步处理取MAX 复杂度mlogn 严格 bzoj1977 严格的话不仅要处理出maxe[i][j]还要处理出次大的maxe2[i][j] 因为当两点之间的边权最大值等于加上的边权的