bzoj4331 JSOI2012 越狱老虎桥

树链剖分 同时被 2 个专栏收录
9 篇文章 0 订阅
1 篇文章 0 订阅

题目链接:bzoj4331
题目大意:
我又看错题了(或者说理解错题意了QAQ)

题意应该是,给一个有N个点M条边的无向连通图,它可能随机在其中加一条边,但是你不知道。问可能的随机加边方案中你割断一条原有边能使整个图不连通的最少花费。

题解:
缩点+链剖
易知,对于一个环来说,就算它加的边不在环中,你任割环上的一边,整个图仍是连通的。所以我们先进行缩点。
然后就(可以)变成了一个树形结构的图。
因为它可以随机加一条边,那么它要想我的花费最多那肯定是尽量把边权小的边都连起来构成环。而第一个(按边权从小到大排序后)无法加入环中的边就是答案。
什么叫无法加入环中呢?它只能加一条边,即连接两个点。而在树中两个点之间的路径是唯一的,是直链状的。所以如果要加入的边加入后使之前边构成的链产生了支链的话即称这条边无法加入环中。
我打了树链剖分来求lca什么的,讨论一下情况就好了。
具体的,,还真很难讲。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 500010

const int inf=1e7;
struct node
{
    int x,y,c,next;bool mk;
}a[maxn*2],tr[maxn*2];
int len,first[maxn],ln,fir[maxn];
int mymin(int x,int y){return (x<y)?x:y;}
int mymax(int x,int y){return (x>y)?x:y;}
void ins(int x,int y,int c)
{
    len++;a[len].x=x;a[len].y=y;a[len].c=c;
    a[len].next=first[x];first[x]=len;a[len].mk=1;
}
int belong[maxn],cnt;
int dfn[maxn],low[maxn],sta[maxn],id,tp;
void tarjan(int x)
{
    dfn[x]=low[x]=++id;
    sta[++tp]=x;
    for (int k=first[x];k!=-1;k=a[k].next) if (a[k].mk)
    {
        int y=a[k].y;
        // if (y==fa) continue;
        if (k&1) a[k].mk=a[k+1].mk=0;
        else a[k].mk=a[k-1].mk=0;
        if (dfn[y]==-1)
        {
            tarjan(y);
            low[x]=mymin(low[x],low[y]);
            if (low[y]>dfn[x])
            {
                cnt++;int z;
                do
                {
                    z=sta[tp--];
                    belong[z]=cnt;
                }while (z!=y);
            }
        }else low[x]=mymin(low[x],dfn[y]);
    }
}
struct edge{int c,id;}ed[maxn*2];int num;
bool cmp(edge x,edge y) {return x.c<y.c;}
void INS(int x,int y,int c)
{
    ln++;tr[ln].x=x;tr[ln].y=y;tr[ln].c=c;
    tr[ln].next=fir[x];fir[x]=ln;
}
int top[maxn],dep[maxn],siz[maxn],son[maxn],fa[maxn];
void dfs(int x)
{
    siz[x]=son[x]=0;
    for (int k=fir[x];k!=-1;k=tr[k].next)
    {
        int y=tr[k].y;
        if (y==fa[x]) continue;
        fa[y]=x;dep[y]=dep[x]+1;
        dfs(y);
        if (siz[y]>siz[son[x]]) son[x]=y;
        siz[x]+=siz[y];
    }
    siz[x]+=1;
}
void dfs2(int x,int ttp)
{
    top[x]=ttp;
    if (son[x]!=0) dfs2(son[x],ttp);
    for (int k=fir[x];k!=-1;k=tr[k].next)
    {
        int y=tr[k].y;
        if (y!=fa[x] && y!=son[x]) dfs2(y,y);
    }
}
int lca(int x,int y)
{
    int tpx=top[x],tpy=top[y];
    while (tpx!=tpy)
    {
        if (dep[tpx]>dep[tpy])
        {
            swap(tpx,tpy);
            swap(x,y);
        }
        y=fa[top[y]];tpy=top[y];
    }
    if (dep[x]>dep[y]) return y;
    return x;
}
bool check(int &lx,int &ly,int xx,int yy)
{
    int la=lca(lx,ly);
    int lc1=lca(lx,xx),lc2=lca(ly,xx);

    if (lc1==lx && lc2==ly) {lx=la,ly=yy;return true;}
    if (la!=xx && lca(la,xx)==xx && (la==lx || la==ly)) {lx=xx,ly=(dep[lx]>dep[ly])?lx:ly;return true;}
    if (lc1==lc2 && (la==lx || la==ly)) {lx=(dep[lx]>dep[ly])?lx:ly,ly=yy;return true;}
    return false;
}
int main()
{
    //freopen("escape.in","r",stdin);
    //freopen("escape.out","w",stdout);
    int n,m,i,x,y,c;
    scanf("%d%d",&n,&m);
    len=0;memset(first,-1,sizeof(first));
    for (i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&c);
        ins(x,y,c);ins(y,x,c);
    }

    tp=id=0;
    for (i=1;i<=n;i++) dfn[i]=-1;
    cnt=0;tarjan(1);num=0;cnt++;
    while (tp) belong[sta[tp--]]=cnt;

    ln=0;memset(fir,-1,sizeof(fir));
    for (i=1;i<=len;i+=2)
     if (belong[a[i].x]!=belong[a[i].y])
     {
        INS(belong[a[i].x],belong[a[i].y],a[i].c);
        ed[++num].c=a[i].c;ed[num].id=ln;
        INS(belong[a[i].y],belong[a[i].x],a[i].c);
     }

    fa[1]=0;dep[1]=0;dfs(1);dfs2(1,1);
    sort(ed+1,ed+1+num,cmp);

    int ans=-1;
    int lx=tr[ed[1].id].x,ly=tr[ed[1].id].y;
    for (i=2;i<=num;i++)
    {
        int xx=tr[ed[i].id].x,yy=tr[ed[i].id].y;
        if (dep[xx]>dep[yy]) swap(xx,yy);
        if (check(lx,ly,xx,yy)) continue;
        ans=ed[i].c;
        break;
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值