zoukankan      html  css  js  c++  java
  • 【BZOJ1969】航线规划(AHOI2005)-边双连通分量+树链剖分

    测试地址:航线规划
    做法:本题需要用到边双连通分量+树链剖分。
    注意到,这题有删边操作,又要维护边双连通分量相关信息(从割边自然想到维护边双连通分量),无法维护。注意到没有强制在线,所以我们可以倒过来,变成有加边操作,同时维护边双连通分量,这样就好做了。
    首先将边双连通分量缩点,缩完后图变为一棵树,而每加一条边,实际上就是把两个端点在树中的路径合并为一个点。虽然维护合并貌似可以用并查集暴力维护(因为总的合并次数不超过n),但是两点间的割边数量就没法求了。于是我们不能实际上做“缩点”这一操作,而是把对应路径上的边权都变成0(默认原先路径上边权都是1),这样两点间路径的和就是它们之间的割边数量了。实际上,这个边权的意义在于,如果两个端点属于同一个边双连通分量,这条边就不是割边,边权就是0,否则它就是割边,边权就是1。这样一来,用树链剖分维护即可,时间复杂度为O(qlog2n)q为操作次数)。
    这题貌似有挺多做法,有一种不用Tarjan,而是用并查集缩点的方法,应该可行。而另一种方法,是在最后还存在的边集中求出一棵生成树,然后暴力加边,看似不用缩点,简单而又非常可做,但要注意的是,这样的时间复杂度是O(mlog2n)的,时限紧的话有可能会被卡掉。
    我傻逼的地方:求边双连通分量的算法写错了……一定要记住这几个Tarjan算法的区别……
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    int n,m,t,td,c[40010],x[40010],y[40010],ans[40010];
    int first[30010]={0},tot=0,totbcc;
    int dfn[30010],low[30010],belong[30010],tim=0,st[30010],top=0;
    int fa[30010],dep[30010],son[30010]={0},siz[30010];
    int pos[30010],postim[30010],tp[30010];
    int seg[120010],tag[120010]={0};
    bool vis[30010]={0};
    struct forsort
    {
        int a,b;
    }p[200010],d[80010];
    struct edge
    {
        int v,next;
    }e[200010];
    
    bool cmp(forsort a,forsort b)
    {
        if (a.a==b.a) return a.b<b.b;
        else return a.a<b.a;
    }
    
    void insert(int a,int b)
    {
        e[++tot].v=b;
        e[tot].next=first[a];
        first[a]=tot;
    }
    
    void init()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&p[2*i-1].a,&p[2*i-1].b);
            p[2*i].a=p[2*i-1].b;
            p[2*i].b=p[2*i-1].a;
        }
        sort(p+1,p+2*m+1,cmp);
    
        t=td=0;
        while(scanf("%d",&c[++t])&&c[t]!=-1)
        {
            scanf("%d%d",&x[t],&y[t]);
            if (!c[t])
            {
                d[++td].a=x[t],d[td].b=y[t];
                d[++td].a=y[t],d[td].b=x[t];
            }
        }
        sort(d+1,d+td+1,cmp);
    
        int x=1;
        for(int i=1;i<=2*m;i++)
        {
            while(x<td&&(d[x].a<p[i].a||(d[x].a==p[i].a&&d[x].b<p[i].b))) x++;
            if (d[x].a==p[i].a&&d[x].b==p[i].b) continue;
            insert(p[i].a,p[i].b);
        }
    }
    
    void dfs(int v,int fa)
    {
        dfn[v]=low[v]=++tim;
        st[++top]=v;
        vis[v]=1;
        int now=top;
        for(int i=first[v];i;i=e[i].next)
            if (e[i].v!=fa)
            {
                if (!vis[e[i].v])
                {
                    dfs(e[i].v,v);
                    low[v]=min(low[v],low[e[i].v]);
                }
                else low[v]=min(low[v],dfn[e[i].v]);
            }
        if (low[v]==dfn[v])
        {
            ++totbcc;
            while(top>=now)
            {
                belong[st[top]]=totbcc;
                top--;
            }
        }
    }
    
    void tarjan()
    {
        totbcc=0;
        dfs(1,0);
    
        memset(first,0,sizeof(first));
        tot=0;
        int x=1;
        for(int i=1;i<=2*m;i++)
        {
            while(x<td&&(d[x].a<p[i].a||(d[x].a==p[i].a&&d[x].b<p[i].b))) x++;
            if (d[x].a==p[i].a&&d[x].b==p[i].b) continue;
            if (belong[p[i].a]!=belong[p[i].b])
                insert(belong[p[i].a],belong[p[i].b]);
        }
    }
    
    void dfs1(int v)
    {
        siz[v]=1;
        for(int i=first[v];i;i=e[i].next)
            if (e[i].v!=fa[v])
            {
                fa[e[i].v]=v;
                dep[e[i].v]=dep[v]+1;
                dfs1(e[i].v);
                if (!son[v]||siz[son[v]]<siz[e[i].v])
                    son[v]=e[i].v;
                siz[v]+=siz[e[i].v];
            }
    }
    
    void dfs2(int v,int top)
    {
        tp[v]=top;
        postim[++tim]=v;
        pos[v]=tim;
        if (son[v]) dfs2(son[v],top);
        for(int i=first[v];i;i=e[i].next)
            if (e[i].v!=fa[v]&&e[i].v!=son[v])
                dfs2(e[i].v,e[i].v);
    }
    
    void pushdown(int no)
    {
        if (tag[no])
        {
            tag[no<<1]=tag[no<<1|1]=tag[no];
            seg[no<<1]=seg[no<<1|1]=0;
            tag[no]=0;
        }
    }
    
    void pushup(int no)
    {
        seg[no]=seg[no<<1]+seg[no<<1|1];
    }
    
    void buildtree(int no,int l,int r)
    {
        if (l==r) {seg[no]=1;return;}
        int mid=(l+r)>>1;
        buildtree(no<<1,l,mid);
        buildtree(no<<1|1,mid+1,r);
        pushup(no);
    }
    
    void segmodify(int no,int l,int r,int s,int t)
    {
        if (l>=s&&r<=t)
        {
            tag[no]=1;
            seg[no]=0;
            return;
        }
        int mid=(l+r)>>1;
        pushdown(no);
        if (s<=mid) segmodify(no<<1,l,mid,s,t);
        if (t>mid) segmodify(no<<1|1,mid+1,r,s,t);
        pushup(no);
    }
    
    int segquery(int no,int l,int r,int s,int t)
    {
        if (l>=s&&r<=t) return seg[no];
        int mid=(l+r)>>1,sum=0;
        pushdown(no);
        if (s<=mid) sum+=segquery(no<<1,l,mid,s,t);
        if (t>mid) sum+=segquery(no<<1|1,mid+1,r,s,t);
        return sum;
    }
    
    int query(int x,int y)
    {
        int ans=0;
        while(tp[x]!=tp[y])
        {
            if (dep[tp[x]]<dep[tp[y]]) swap(x,y);
            ans+=segquery(1,1,totbcc,pos[tp[x]],pos[x]);
            x=fa[tp[x]];
        }
        if (dep[x]>dep[y]) swap(x,y);
        if (x!=y) ans+=segquery(1,1,totbcc,pos[x]+1,pos[y]);
        return ans;
    }
    
    void modify(int x,int y)
    {
        while(tp[x]!=tp[y])
        {
            if (dep[tp[x]]<dep[tp[y]]) swap(x,y);
            segmodify(1,1,totbcc,pos[tp[x]],pos[x]);
            x=fa[tp[x]];
        }
        if (dep[x]>dep[y]) swap(x,y);
        if (x!=y) segmodify(1,1,totbcc,pos[x]+1,pos[y]);
    }
    
    void work()
    {
        buildtree(1,1,totbcc);
        t--;
        for(int i=t;i>=1;i--)
        {
            if (c[i]) ans[i]=query(belong[x[i]],belong[y[i]]);
            else modify(belong[x[i]],belong[y[i]]);
        }
        for(int i=1;i<=t;i++)
            if (c[i]) printf("%d
    ",ans[i]);
    }
    
    int main()
    {
        init();
        tarjan();
        fa[n+1]=dep[n+1]=0;
        dfs1(1);
        tim=0;
        dfs2(1,1);
        work();
    
        return 0;
    }
  • 相关阅读:
    PHP防跨站之open_basedir目录设置
    在线Jupyter平台,验证手机号可免费使用
    wget命令之用户密码cookies断点续传指定类型整站下载
    linux控制台输出到文件
    结构体取变量
    常用编码解码识别
    #define和const
    做一个自己的清理临时文件的小程序(附成品&升级版)
    C盘清理/瘦身
    修复Word启动失败 报错WPS32
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793340.html
Copyright © 2011-2022 走看看