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

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1977

因为严格,所以要记录到 LCA 的一个次小值;

很快写好,然后改掉一堆错误后终于过了样例!然而交上去1WA;

又改了半天,还是WA,于是放弃,抄题解好久...

然而就在我调了一个小时终于锁定错误就在那个子函数里的时候才突然看到了自己的明显惊天大错误是怎么回事??!!!稍微改了一下下就完美AC。。。

不过还有点收获,把求各种层次的 f 放在 dfs 函数里会比单独拿出来再求一遍快 1000+ms 哦~

题解的写法是直接看 u 到 lca 和 v 到 lca 路上的值,而我是求 lca 的过程中求值的,还是喜欢我的写法;

题解的写法放在注释里了;

囧。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int const maxn=1e5+5,maxm=3e5+5,inf=1e9;
int n,m,f[maxn][22],dep[maxn],mx[maxn][22],nmx[maxn][22];//22
int hd[maxn],ct,fa[maxn],d[maxn],mnn;
ll ans;//ll
bool vis[maxn],used[maxm];
struct P{int u,v,w;}e[maxm];
struct T{
    int mx,nmx;
    T(int x=0,int n=0):mx(x),nmx(n) {}
};
struct N{
    int to,nxt,w;
    N(int t=0,int n=0,int w=0):to(t),nxt(n),w(w) {}
}ed[maxn<<1];
bool cmp(P x,P y){return x.w<y.w;}
void add(int x,int y,int w){ed[++ct]=N(y,hd[x],w); hd[x]=ct;}
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void dfs(int x)
{
    for (int i=1; i<=20; i++)//写在这里较快
        {
            if(dep[x]<(1<<i))break;
            f[x][i]=f[f[x][i-1]][i-1];
            mx[x][i]=max(mx[x][i-1],mx[f[x][i-1]][i-1]);
            if (mx[x][i-1]==mx[f[x][i-1]][i-1])
                nmx[x][i]=max(nmx[x][i-1],nmx[f[x][i-1]][i-1]);
            else
                nmx[x][i]=min(mx[x][i-1],mx[f[x][i-1]][i-1]),
                nmx[x][i]=max(nmx[x][i],nmx[x][i-1]),
                nmx[x][i]=max(nmx[x][i],nmx[f[x][i-1]][i-1]);
        }
    vis[x]=1;
    for(int i=hd[x],u;i;i=ed[i].nxt)
        if(!vis[u=ed[i].to])
        {
            f[u][0]=x; dep[u]=dep[x]+1;
            mx[u][0]=ed[i].w;
//            nmx[u][0]=ed[i].w;//无!
            d[u]=ed[i].w;
            dfs(u);
        }
}
void init()
{
    dep[1]=1; dfs(1);
//    for(int j=1;j<=20;j++)
//        for(int i=1;i<=n;i++)
//        {
//            int tmx=mx[f[i][j-1]][j-1],tnx=nmx[f[i][j-1]][j-1];
//            f[i][j]=f[f[i][j-1]][j-1];
//            mx[i][j]=max(mx[i][j-1],tmx);
//            if(tmx>mx[i][j-1]) nmx[i][j]=max(mx[i][j-1],tnx);
//            else if(tmx==mx[i][j-1]) nmx[i][j]=max(nmx[i][j-1],tnx);
//            else nmx[i][j]=max(nmx[i][j-1],tmx);
//        }
}
void ch(int &rmx,int &rnx,int tmx,int tnx)
{
    if(tmx>rmx)
    {
        rnx=max(tnx,rmx);
        rmx=tmx;
    }
    else if(tmx==rmx) rnx=max(tnx,rnx);
    else rnx=max(rnx,tmx);
}
T lca(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    int k=dep[x]-dep[y],rmx=-1,rnx=-1;
    for(int i=20;i>=0;i--)
//        if((1<<i)&k){rmx=mx[x][i]; rnx=nmx[x][i]; x=f[x][i];}//x放在后面修改!
        if((1<<i)&k){ch(rmx,rnx,mx[x][i],nmx[x][i]); x=f[x][i];}//!!!!!!囧
    for(int i=20;i>=0;i--)
        if(f[x][i]!=f[y][i])
        {
            ch(rmx,rnx,mx[x][i],nmx[x][i]);
            ch(rmx,rnx,mx[y][i],nmx[y][i]);
            x=f[x][i]; y=f[y][i]; //后面改!!
        }
    if(x!=y) ch(rmx,rnx,max(d[x],d[y]),min(d[x],d[y]));
    return T(rmx,rnx);
}
//int LCA(int x,int y)
//{
//    if(dep[x]<dep[y])swap(x,y);
//    int k=dep[x]-dep[y];
//    for(int i=20;i>=0;i--)
//        if((1<<i)&k){rmx=mx[x][i]; rnx=nmx[x][i]; x=f[x][i];}
//    for(int i=20;i>=0;i--)
//        if(f[x][i]!=f[y][i])
//        {
//            x=f[x][i]; y=f[y][i];
//        }
//    if(x==y)return x;
//    return f[x][0];
//}
//T work(int x,int lca)
//{
//    T ret;
//    int maxx1=-1,maxx2=-1;
//    int d=dep[x]-dep[lca];
//    for (int i=0; i<=20; i++)
//        if (d&(1<<i))
//            {
//                ch(maxx1,maxx2,mx[x][i],nmx[x][i]);
//                x=f[x][i];
//            }
//    ret.mx=maxx1,ret.nmx=maxx2;
//    return ret;
//}
//T solve(int x)
//{
//    T ret;
//    int u=e[x].u,v=e[x].v,lca=LCA(u,v);
//    T uu=work(u,lca);
//    T vv=work(v,lca);
//    ret.mx=max(uu.mx,vv.mx);
//    if(uu.mx>vv.mx) ret.nmx=max(vv.mx,uu.nmx);
//    else if(uu.mx==vv.mx) ret.nmx=max(vv.nmx,uu.nmx);
//    else ret.nmx=max(uu.mx,vv.nmx);
//    return ret;
//}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=n;i++)fa[i]=i;
    int cnt=0;
    for(int i=1,u,v;i<=m;i++)
        if(find(u=e[i].u)!=find(v=e[i].v))
        {
            cnt++;
            fa[find(u)]=find(v);
            used[i]=1; ans+=e[i].w;
            add(u,v,e[i].w); add(v,u,e[i].w);
            if(cnt==n-1)break;
        }
    init();
    mnn=inf;
    for(int i=1;i<=m;i++)
    {
        if(used[i])continue;
        int u=e[i].u,v=e[i].v;
        T l=lca(u,v);
//        T l=solve(i);
        if(e[i].w==l.mx)mnn=min(mnn,e[i].w-l.nmx);
        else mnn=min(mnn,e[i].w-l.mx);
    }
    printf("%lld",ans+mnn);
    return 0;
}

原文地址:https://www.cnblogs.com/Zinn/p/9275905.html

时间: 2024-10-10 01:04:35

bzoj1977 [BeiJing2010组队]次小生成树 Tree——严格次小生成树的相关文章

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

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

【BZOJ 1977】 [BeiJing2010组队]次小生成树 Tree

1977: [BeiJing2010组队]次小生成树 Tree Time Limit: 10 Sec  Memory Limit: 512 MB Submit: 2313  Solved: 544 [Submit][Status][Discuss] Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了.小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格

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

题解:和cf的一道题比较类似 首先跑一个MST 对于这个树做树链剖分 枚举不在这个树上的边找严格小于这条边的最大边权值 然后求ans #include <bits/stdc++.h> #define ll long long const int MAXN=1e5+10; const int maxn=3e5+10; const int inf=1e9+20; using namespace std; ll read(){ ll x=0,f=1;char ch=getchar(); while(

BZOJ1976: [BeiJing2010组队]能量魔方 Cube

1976: [BeiJing2010组队]能量魔方 Cube Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 832  Solved: 281[Submit][Status] Description 小C 有一个能量魔方,这个魔方可神奇了,只要按照特定方式,放入不同的 能量水晶,就可以产生巨大的能量. 能量魔方是一个 N*N*N 的立方体,一共用 N3 个空格可以填充能量水晶. 能量水晶有两种: ·一种是正能量水晶(Positive) ·一种是负能量

【BZOJ1976】[BeiJing2010组队]能量魔方 Cube 最小割

[BZOJ1976][BeiJing2010组队]能量魔方 Cube Description 小C 有一个能量魔方,这个魔方可神奇了,只要按照特定方式,放入不同的 能量水晶,就可以产生巨大的能量. 能量魔方是一个 N*N*N 的立方体,一共用 N3 个空格可以填充能量水晶. 能量水晶有两种: ·一种是正能量水晶(Positive) ·一种是负能量水晶(Negative) 当这个魔方被填满后,就会依据填充的能量水晶间的关系产生巨大能量.对 于相邻两(相邻就是拥有同一个面)的两个格子,如果这两个格子

洛谷P4180 [Beijing2010组队]次小生成树Tree(最小生成树,LCT,主席树,倍增LCA,倍增,树链剖分)

洛谷题目传送门 %%%天平巨佬和山楠巨佬%%% 他们的题解 思路分析 具体思路都在两位巨佬的题解中.这题做法挺多的,我就不对每个都详细讲了,泛泛而谈吧. 首先kruskal把最小生成树弄出来,因为要求次小生成树.至于为什么次小一定只在最小的基础上改变了一条边,我也不会证......打表找规律大法好 剩下的可以有一堆数据结构来维护最大值和次大值(原理两位巨佬都讲清楚了,这里只分析一下算法的优劣) 倍增+LCA 山楠巨佬的做法,我也写了这一种.复杂度\(O(MlogM(kruscal)+MlogN(

[BeiJing2010组队]次小生成树 Tree

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

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

严格次小生成树.一开始没有特批一圈都相等的情况,一直WA,十分难受. 先生成最小生成树,枚举每条非树边,连上它构成一个环,拆掉环上树边中最大的一条(若和该边相等则次大的一条)换上这条. 用倍增维护一条链上的最大边和次大边,倍增跑lca同时找出环上最大边和次大边,看能否更新答案. #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cmath

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

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