zoukankan      html  css  js  c++  java
  • LCA&最小生成树

    LCA 经常被用来使用。比如询问树两点之间的距离。

    比如树上差分 都是经常被使用的类型。有的时候倍增求LCA的同时还可以优化算法。

    这道题呢 求一个严格的最小生成树,当然如果不严格的话如果有重边那么就和原来一样喽。

    这里推广一些关于最小生成树的定理 在图中 最小生成树边权一定是一定的。

    那么由此可以推出不同的最小生成树也就是边不同但是他们被连在图中的边权一定相等。

    所以上述结论和这道题没什么关系 关键是严格的次小生成树 。

    我们可以加边 来完成这整个操作,考虑到对于每一条边造成的影响还是这两点之间的最短距离的影响。仔细想就知道了。

    那么影响转化成了他们到LCA这些边的影响,LCA之外的边可就没什么事了。

    我们只需要求出他们到LCA的最长边有多大即可。如果相等了那么没用了么?此时必须要维护一个次小的边权已被不时只需。

    建模成功! 我们把问题成功转换成了 求两点之间到LCA之间的最大边权 严格次大边权。

    LCA 可以tarjan,复杂度非常优秀可是考虑到 一些问题,求不出边权 先求出祖先再往上爬不就好了?

    这样写有些 复杂度nm 并不能通过此题。考虑倍增求出LCA

    在往上跳跃的时候我们维护一个最大值边权和次大值边权即可。复杂度mlgn

    //#include<bits/stdc++.h>
    #include<iostream>
    #include<cstdio>
    #include<iomanip>
    #include<cstring>
    #include<string>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<cctype>
    #include<utility>
    #include<set>
    #include<bitset>
    #include<queue>
    #include<stack>
    #include<deque>
    #include<map>
    #include<vector>
    #include<ctime>
    #define ll long long
    #define INF 2147483646
    #define z(i) t[i].z
    using namespace std;
    char buf[1<<15],*fs,*ft;
    inline char getc()
    {
        return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    inline ll read()
    {
        ll x=0,f=1;char ch=getc();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
        return x*f;
    }
    inline void put(ll x)
    {
        x<0?putchar('-'),x=-x:0;
        ll num=0;char ch[50];
        while(x)ch[++num]=x%10+'0',x/=10;
        num==0?putchar('0'):0;
        while(num)putchar(ch[num--]);
        putchar(' ');return;
    }
    const ll MAXN=300002,maxn=100002;
    ll n,m,sum,cnt,T,maxx,maxx1,minn;
    ll lin[MAXN<<1],nex[MAXN<<1],ver[MAXN<<1],e[MAXN<<1],len=0;
    ll clin[MAXN<<1],cnex[MAXN<<1],cver[MAXN<<1],ce[MAXN<<1],clen=0;
    ll dis[maxn],depth[maxn],vis[maxn],f[maxn][18],g[maxn][18][2];//f[i][k]表示i节点向上爬2^k个节点
    //g[i][k][0/1]表示i节点向上爬2^k个节点之中的最大值 严格次大值
    priority_queue<pair<ll,pair<ll,ll> > > q;
    inline ll max(ll x,ll y){return x>y?x:y;}
    inline ll min(ll x,ll y){return x>y?y:x;}
    inline void swap(ll &x,ll &y){ll t;t=x;x=y;y=t;}
    struct wy{ll x,y,z;}t[MAXN];
    void add(ll x,ll y,ll z)
    {
        ver[++len]=y;
        nex[len]=lin[x];
        lin[x]=len;
        e[len]=z;
    }
    void cadd(ll x,ll y,ll z)
    {
        cver[++clen]=y;
        cnex[clen]=clin[x];
        clin[x]=clen;
        ce[clen]=z;
    }
    void prim()
    {
        for(ll i=1;i<=n;i++)dis[i]=INF;
        memset(vis,0,sizeof(vis));
        dis[1]=0;ll flag=0;
        q.push(make_pair(dis[1],make_pair(1,0)));
        while(q.size())
        {
            ll te=q.top().second.first;
            ll yy=q.top().second.second;
            q.pop();
            if(vis[te]==1)continue;
            vis[te]=1;sum+=dis[te];
            ++flag;if(flag!=1)cadd(te,yy,dis[te]),cadd(yy,te,dis[te]);
            for(ll i=lin[te];i;i=nex[i])
            {
                ll tn=ver[i];
                if(vis[tn]==1)continue;
                if(dis[tn]>e[i])
                {
                    dis[tn]=e[i];
                    q.push(make_pair(-dis[tn],make_pair(tn,te)));
                }
            }
        }
    }
    void dfs(ll x,ll fa)
    {
        depth[x]=depth[fa]+1;
        f[x][0]=fa;
        for(ll i=1;i<=T;++i)
        {    
            f[x][i]=f[f[x][i-1]][i-1];
            g[x][i][0]=max(g[x][i-1][0],g[f[x][i-1]][i-1][0]);
            if(g[x][i-1][0]==g[f[x][i-1]][i-1][0])g[x][i][1]=max(g[x][i-1][1],g[f[x][i-1]][i-1][1]);
            if(g[x][i-1][0]>g[f[x][i-1]][i-1][0])g[x][i][1]=max(g[x][i-1][1],g[f[x][i-1]][i-1][0]);
            if(g[x][i-1][0]<g[f[x][i-1]][i-1][0])g[x][i][1]=max(g[x][i-1][0],g[f[x][i-1]][i-1][1]);
        }
        for(ll i=clin[x];i;i=cnex[i])
        {
            ll tn=cver[i];
            if(tn==fa)continue;
            g[tn][0][0]=ce[i];
            g[tn][0][1]=-INF;
            dfs(tn,x);
        }
    }
    void lca(ll x,ll y)
    {
        if(depth[x]>depth[y])swap(x,y);//y>x
        for(ll i=T;i>=0;--i)
        {
            if(depth[f[y][i]]>=depth[x])
            {
                if(maxx>g[y][i][0])maxx1=max(maxx1,g[y][i][0]);
                if(maxx<g[y][i][0])maxx1=max(maxx,maxx1);
                maxx=max(maxx,g[y][i][0]);
                maxx1=max(maxx1,g[y][i][1]);
                y=f[y][i];
            }
        }
        if(x==y)return;
        for(ll i=T;i>=0;--i)
        {
            if(f[x][i]!=f[y][i])
            {
                if(maxx>g[y][i][0])maxx1=max(maxx1,g[y][i][0]);
                if(maxx<g[y][i][0])maxx1=max(maxx,maxx1);
                if(maxx>g[x][i][0])maxx1=max(maxx1,g[x][i][0]);
                if(maxx<g[x][i][0])maxx1=max(maxx,maxx1);
                maxx=max(maxx,g[x][i][0]);
                maxx1=max(maxx1,g[x][i][1]);
                maxx=max(maxx,g[y][i][0]);
                maxx1=max(maxx1,g[y][i][1]);
                x=f[x][i];y=f[y][i];
            }
        }
        if(maxx>g[y][0][0])maxx1=max(maxx1,g[y][0][0]);
        if(maxx<g[y][0][0])maxx1=max(maxx,maxx1);
        if(maxx>g[x][0][0])maxx1=max(maxx1,g[x][0][0]);
        if(maxx<g[x][0][0])maxx1=max(maxx,maxx1);
        maxx=max(maxx,g[x][0][0]);
        maxx=max(maxx,g[y][0][0]);
        return;
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();m=read();
        for(ll i=1;i<=m;++i)
        {
            ll x,y,z;
            x=read();y=read();z=read();
            t[i].x=x;t[i].y=y;z(i)=z;
            add(x,y,z);add(y,x,z);
        }
        prim();
        //put(sum);
        T=(ll)(log(n*1.0)/log(2*1.0))+1;
        //put(T);
        dfs(1,0);minn=INF;
        for(ll i=1;i<=m;++i)
        {
            maxx=maxx1=-INF;
            lca(t[i].x,t[i].y);
            if(maxx==z(i))minn=min(minn,z(i)-maxx1);
            else minn=min(minn,z(i)-maxx);
        }
        put(sum+minn);
        return 0;
    }
    View Code

    这道题呢 很有意思但是我并没有观察到问题的特异性 。

    可能是 写题的时候大脑太累了 不思考了,以后累的时候一定要休息一会效率不但高而且很敏锐的就可以破解问题。

    注意 最最小生成树的个数 每个最小生成树相同边权的个数一定是相同的,因为考虑如果有一个更优的边权可以加到图中,我的最小生成树还是最小生成树么,

    那如果是刚好有一条边让出来位置呢,那么我的最小生成树边权的相同边权的个数还是一定的。证毕。

    那么 根据前面一道题我的废话 就可以得到一个算法了 前面的废话是一些点 被连在最小生成树上的边权一定是相等的。

    也就是联通块每次都是一样的只不过点连到联通块的方式不太一样。那么既然n只有100 也就是边至多99条。

    且每条边权值只有10条 那么 爆搜在最小生成树上的每一条边即可。

    复杂度 2^n*n 多么完美啊,加上玄学剪枝 直接完爆正解!!!

    //#include<bits/stdc++.h>
    #include<iostream>
    #include<cstdio>
    #include<iomanip>
    #include<cstring>
    #include<string>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<cctype>
    #include<utility>
    #include<set>
    #include<bitset>
    #include<queue>
    #include<stack>
    #include<deque>
    #include<map>
    #include<vector>
    #include<ctime>
    #define ll long long
    #define INF 2147483647
    #define z(i) t[i].z
    #define x(i) t[i].x
    #define y(i) t[i].y
    #define mod 31011
    using namespace std;
    char buf[1<<15],*fs,*ft;
    inline char getc()
    {
        return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    inline int read()
    {
        int x=0,f=1;char ch=getc();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
        return x*f;
    }
    inline void put(int x)
    {
        x<0?putchar('-'),x=-x:0;
        int num=0;char ch[50];
        while(x)ch[++num]=x%10+'0',x/=10;
        num==0?putchar('0'):0;
        while(num)putchar(ch[num--]);
        putchar(' ');return;
    }
    const int MAXN=1002;
    int n,m,ans,cnt,sum=1,w;
    int f[MAXN];
    struct wy
    {
        int x,y,z;
        friend int operator <(const wy &p,const wy &u)
        {
            return p.z<u.z;
        }
    }t[MAXN];
    struct wy1{int x,y,k,v;}b[MAXN];
    int gf(int x){return x==f[x]?x:f[x]=gf(f[x]);}
    int getfather(int x){return x==f[x]?x:getfather(f[x]);}
    void dfs(int x,int depth,int s,int k)
    {
        if(s+depth-x+1<k)return;
        if(s==k){ans++;return;}
        if(x==depth+1)return;
        dfs(x+1,depth,s,k);
        int xx=getfather(t[x].x);
        int yy=getfather(t[x].y);
        if(xx!=yy)
        {
            f[xx]=yy;
            dfs(x+1,depth,s+1,k);
            f[xx]=xx;f[yy]=yy;
        }
        return;
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();m=read();
        for(int i=1;i<=m;++i)
        {
            x(i)=read();
            y(i)=read();
            z(i)=read();
        }
        for(int i=1;i<=n;++i)f[i]=i;
        sort(t+1,t+1+m);
        for(int i=1;i<=m;++i)
        {
            if(i==1)b[++cnt].x=i;
            else if(z(i)!=z(i-1))b[cnt].y=i-1,b[++cnt].x=i;
            int xx=gf(x(i));
            int yy=gf(y(i));
            if(xx!=yy)
            {
                f[xx]=yy;ans+=z(i);
                ++b[cnt].k;++w;
            }
        }
        b[cnt].y=m;
        if(w!=n-1){put(0);return 0;}
        //put(cnt);
        //put(ans);
        //for(int i=1;i<=cnt;++i)put(b[i].k);
        for(int i=1;i<=n;++i)f[i]=i;
        for(int i=1;i<=cnt;++i)
        {
            if(b[i].k)
            {
                ans=0;
                dfs(b[i].x,b[i].y,0,b[i].k);
                sum=(sum*(ans%mod))%mod;
                for(int j=b[i].x;j<=b[i].y;++j)
                {
                    int xx=gf(t[j].x);
                    int yy=gf(t[j].y);
                    f[xx]=yy;
                }
            }
        }
        put(sum);
        return 0;
    }
    View Code
  • 相关阅读:
    wmq的A×B Problem
    MATLAB 求系统的单位冲击响应及单位阶跃响应
    关于共享率过低的一些事
    Vue组件之间的通信
    浏览器支持ES6的import和export
    Vue axios拦截问题
    开屏倒计时
    git常用操作
    原型
    this指向问题
  • 原文地址:https://www.cnblogs.com/chdy/p/10573757.html
Copyright © 2011-2022 走看看