【HDU】5458 Stability(2015 ACM/ICPC Shenyang Online)

Stability


题目链接


题目大意

    给你一个图,现在有一系列操作,会删去一些边,然后问你从a到b有几条边是删去之后a和b就不联通了。(题中的Stability)


题解

数链剖分

    假设从a到b有环,a到b的查询肯定是0。因为删掉任何一条边a和b,a和b还是相连的,但是只要a跟b没有环。a到b则至少有一条边是满足题意的(至少一条的意思是a到b的路径中可能有其他的边在环中),题目中说最后的图也是连通的,如此分析,我们可以想到建一颗树,然后在树上查询。下面写具体做法:
    删掉边会破坏环,所以在这一题中为了方便处理,我们把询问和删除操作倒过来处理。我们首先把要删除的边从图中全都删去,然后建一颗树,所有的边权设为1,因为最后可能是图,所以我们把多余的边(成环的边)所成的环的权值全部设为0,在树上看其实就是把从a到b的一条路径全部设置为0了,然后我们从操作的最后开始,查询ab实际上就是查询a到b的边权和,插入ab实际上就是把ab的路径边全设为0。最后输出就行了。
    查询和插入用数链剖分离线处理。


代码

    代码略长。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <set>
#include <map>

using namespace std;

struct question
{
    int a,b,c;
};
question que[100005];
struct tree
{
    int l,r,val;
    bool f;
};
tree d[120005];
multiset< pair<int,int> > S,V;
multiset< pair<int,int> >::iterator it;
int T,n,m,q,num;
int father[50005],top[50005],fat[50005],pos[50005],son[50005],siz[50005],dep[50005],rpos[50005];
vector<int> G[50005],A;

int findfather(int k)
{
    if (father[k]==k) return k;
    else return father[k]=findfather(father[k]);
}

void dfs(int u,int F,int depth)
{
    siz[u]=1; fat[u]=F; dep[u]=depth; son[u]=0;
    for (int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if (v!=F)
        {
            dfs(v,u,depth+1);
            siz[u]+=siz[v];
            if (siz[v]>siz[son[u]]) son[u]=v;
        }
    }
}

void tcp(int u,int t)
{
    pos[u]=num++; rpos[num-1]=u; top[u]=t;
    if (son[u]) tcp(son[u],t);
    for (int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if (v!=fat[u] && v!=son[u]) tcp(v,v);
    }
}

void buildtree(int n,int l,int r)
{
    d[n].l=l; d[n].r=r; d[n].val=0; d[n].f=0;
    if (l==r)
    {
        d[n].val=1;
        return ;
    }
    int mid=(l+r)>>1;
    buildtree(n*2,l,mid);
    buildtree(n*2+1,mid+1,r);
    d[n].val=d[n*2].val+d[n*2+1].val;
}

int Count(int n,int l,int r)
{
    if (d[n].f) return 0;
    if (d[n].l==l && d[n].r==r) return d[n].val;
    int mid=(d[n].l+d[n].r)>>1;
    if (r<=mid) return Count(n*2,l,r);
    else if (l>mid) return Count(n*2+1,l,r);
    else return Count(n*2,l,mid)+Count(n*2+1,mid+1,r);
}

void Insert(int n,int l,int r)
{
    if (d[n].f) return ;
    if (d[n].l==l && r==d[n].r)
    {
        d[n].f=1;
        d[n].val=0;
        return ;
    }
    int mid=(d[n].l+d[n].r)>>1;
    if (r<=mid) Insert(n<<1,l,r);
    else if (l>mid) Insert(n<<1|1,l,r);
    else
    {
        Insert(n<<1,l,mid);
        Insert(n<<1|1,mid+1,r);
    }
    d[n].val=d[n*2].val+d[n*2+1].val;
}

void solve(int u,int v)
{
    int topu=top[u],topv=top[v];
    while (topv!=topu)
    {
        if (dep[topu]<dep[topv])
        {
            swap(u,v);
            swap(topu,topv);
        }
        Insert(1,pos[topu],pos[u]);
        u=fat[topu];
        topu=top[u];
    }
    if (u==v) return ;
    else
    {
        if (dep[u]>dep[v]) swap(u,v);
        Insert(1,pos[son[u]],pos[v]);
    }
}

int answer(int u,int v)
{
    int ans=0;
    int topu=top[u],topv=top[v];
    while (topv!=topu)
    {
        if (dep[topu]<dep[topv])
        {
            swap(u,v);
            swap(topu,topv);
        }
        ans+=Count(1,pos[topu],pos[u]);
        u=fat[topu];
        topu=top[u];
    }
    if (u==v) return ans;
    else
    {
        if (dep[u]>dep[v]) swap(u,v);
        ans+=Count(1,pos[son[u]],pos[v]);
    }
    return ans;
}

int main()
{
    int Case=1;
    scanf("%d",&T);
    while (T--)
    {
        int u,v;
        S.clear(); num=0; V.clear();
        memset(father,0,sizeof(father));
        memset(que,0,sizeof(que));
        memset(siz,0,sizeof(siz));
        memset(son,0,sizeof(son));
        memset(dep,0,sizeof(dep));
        memset(top,0,sizeof(top));
        memset(pos,0,sizeof(pos));
        memset(rpos,0,sizeof(rpos));
        memset(d,0,sizeof(d));
        memset(fat,0,sizeof(fat));
        for (int i=0;i<=100000;i++) G[i].clear();
        A.clear();

        scanf("%d%d%d",&n,&m,&q);
        for (int i=0;i<=n;i++) father[i]=i;

        for (int i=0;i<m;i++)
        {
            scanf("%d%d",&u,&v);
            if (u>v) swap(u,v);
            S.insert(pair<int,int> (u,v) );
        }
        for (int i=0;i<q;i++)
        {
            scanf("%d%d%d",&que[i].a,&que[i].b,&que[i].c);
            if (que[i].b>que[i].c) swap(que[i].b,que[i].c);
            if (que[i].a==1) S.erase( S.find(pair<int,int> (que[i].b,que[i].c)) );
        }
        for (it=S.begin();it!=S.end();it++)
        {
            int F1=it->first,F2=it->second;
            int f1=findfather(F1),f2=findfather(F2);
            if (f1!=f2)
            {
                V.insert(*it);
                if (f1>f2) swap(f1,f2);
                father[f2]=f1;
                G[F1].push_back(F2);
                G[F2].push_back(F1);
            }
        }
        dfs(1,0,0);
        tcp(1,1);
        buildtree(1,0,num-1);
        for (it=S.begin();it!=S.end();it++) if (V.find(*it)==V.end())
        {
            int F1=it->first,F2=it->second;
            solve(F1,F2);
        }
        for (int i=q-1;i>=0;i--)
        {
            int a=que[i].a,b=que[i].b,c=que[i].c;
            if (a==1) solve(b,c);
            else if (a==2) A.push_back(answer(b,c));
        }
        printf("Case #%d:\n",Case++);
        for (int i=A.size()-1;i>=0;i--) printf("%d\n",A[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值