【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\n",ans[i]);
}

int main()
{
    init();
    tarjan();
    fa[n+1]=dep[n+1]=0;
    dfs1(1);
    tim=0;
    dfs2(1,1);
    work();

    return 0;
}
posted @ 2018-06-09 18:18  Maxwei_wzj  阅读(95)  评论(0编辑  收藏  举报