• Luogu P1967 货车运输


    现在开始正式填以前欠下的一些题解。就从这道经典的NOIp题开始讲吧。

    我们仔细看题目,发现要求的是图上两点((u,v))之间的路径上最小值的最大值。

    跑DP?图上状态太多了,单次要(O(n))的复杂度,直接T飞。

    我们考虑一种经典方法:将图转化为一颗树来做

    由于树保证联通,而这里要求最大化最小值,因此我们很容易想到把最大瓶颈生成树找出来

    然后在树上维护信息?那么暴力树剖维护最大值?复杂度双(log)。有点危险。

    再优化一波,我们用树上倍增和维护倍增LCA的方法一样维护最大值,对于所有询问的两个点直接一起跳到它们的LCA即可

    注意图可能不连通,不过我们前面的并查集就可以直接用来判连通性了。

    CODE

    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int N=10005,P=15;
    struct data
    {
        int l,r,s;
    }a[N*5];
    int head[N],n,m,q,x,y,father[N],dep[N],par[N][P],f[N][P],cnt;
    bool use[N];
    struct edge
    {
        int to,next,v;
    }e[N<<1];
    inline char tc(void)
    {
        static char fl[100000],*A=fl,*B=fl;
        return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
        x=0; char ch; while (!isdigit(ch=tc()));
        while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void write(int x)
    {
        if (x>9) write(x/10);
        putchar(x%10+'0');	
    }
    inline int getfather(int k)
    {
        return father[k]^k?father[k]=getfather(father[k]):k;
    }
    inline bool cmp(data a,data b)
    {
        return a.s>b.s;
    }
    inline void double_add(int x,int y,int z)
    {
        e[++cnt].to=y; e[cnt].next=head[x]; e[cnt].v=z; head[x]=cnt;
        e[++cnt].to=x; e[cnt].next=head[y]; e[cnt].v=z; head[y]=cnt;
    }
    inline int min(int a,int b)
    {
        return a<b?a:b;
    }
    inline void MST(void)
    {
        for (register int i=1;i<=n;++i)
        father[i]=i;
        for (register int i=1;i<=m;++i)
        {
            int fx=getfather(a[i].l),fy=getfather(a[i].r);
            if (fx!=fy) double_add(a[i].l,a[i].r,a[i].s),father[fx]=fy;
        }
    }
    inline void reset(int now)
    {
        for (register int i=0;i<P-1;++i)
        if (par[now][i]) par[now][i+1]=par[par[now][i]][i],f[now][i+1]=min(f[now][i],f[par[now][i]][i]); else break;
    }
    inline void DFS(int now,int fa,int d)
    {
        dep[now]=d; par[now][0]=fa; reset(now);
        for (register int i=head[now];i!=-1;i=e[i].next)
        if (e[i].to!=fa) f[e[i].to][0]=e[i].v,DFS(e[i].to,now,d+1);
    }
    inline void swap(int &a,int &b)
    {
        int t=a; a=b; b=t;
    }
    inline int get_min(int x,int y)
    {
        int res=1e9; if (dep[x]<dep[y]) swap(x,y);
        for (register int i=P-1;i>=0;--i)
        if (par[x][i]&&dep[par[x][i]]>=dep[y]) res=min(res,f[x][i]),x=par[x][i];
        if (x==y) return res;
        for (register int i=P-1;i>=0;--i)
        if (par[x][i]!=par[y][i])
        {
            res=min(res,min(f[x][i],f[y][i]));
            x=par[x][i]; y=par[y][i];
        }
        return min(res,min(f[x][0],f[y][0]));
    }
    int main()
    {
        //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
        register int i; read(n); read(m);
        memset(head,-1,sizeof(head)); memset(e,-1,sizeof(e));
        for (i=1;i<=m;++i)
        read(a[i].l),read(a[i].r),read(a[i].s);	
        sort(a+1,a+m+1,cmp); MST(); read(q);
        for (i=1;i<=n;++i)
        {
            int fa=getfather(i);
            if (!use[fa]) DFS(i,0,0),use[fa]=1;
        }
        while (q--)
        {
            read(x); read(y);
            if (getfather(x)!=getfather(y)) puts("-1");
            else write(get_min(x,y)),putchar('
    ');
        }
        return 0;
    }
    
  • 相关阅读:
    linux ramdisk
    linux系统灵活运用灯[android课程3]
    linux编译注解
    Linux内核3.0移植并基于Initramfs根文件系统启动
    linux0.11文件分析
    2.2linux内核移植简介
    sudo: ./sd_fusing.sh:找不到命令
    Tiny4412之C语言实现流水灯,Tiny4412裸机程序[3]
    Exynos 4412的启动过程分析[2]
    POJ 3009 Curling 2.0 {深度优先搜索}
  • 原文地址:https://www.cnblogs.com/cjjsb/p/9326783.html
Copyright © 2020-2023  润新知