2015 ACM/ICPC Asia Regional Shenyang OnlineHDU-5458 Stability

Stability

啊,极其恶心的题。Tarjan缩点+树剖+LCA+线段树

题意:定义两个点之间的稳定度为一个点到另外一个点必须经过的边的条数。给你一个图,Q次操作,每次删除一条边或者查询两个点之间的稳定度。

正向考虑根本无法解决,因为有动态删边的过程,查询两个点的ans几乎就是O(n)。所以我们要反向考虑,题目保证最后的图联通,即最后的图可能是一棵树或者存在环,我们知道一个环中的点的稳定度都为0,关键就在任意两个点之间桥的数量。所以我们需要将最后的图强连通缩点,缩点后肯定是一棵树,每个点代表一个联通块,那么反向考虑, 查询的时候只需查询两个联通块之间的距离(边的条数),如果在一个连通块里面直接输出0。如果是删边操作,因为我们反向考虑, 所以是加边操作,如果两个点在同一个联通块里面不用操作,反之,需要将这条路径上的边全部赋0。也即是借助树链剖分将树转化成线性结构,然后借助线段树或树状数组快速更新或查询区间。我们将原始的边都赋为1,合并只需将两个联通块所在的路径上的边权值赋为0即可。

但我们得知道最后的图,如果用map处理会超时,博主血的教训。网上的题解都是用multiset删除边,于是改了改951ms AC。

const int N=3e4+10;
int n,m,q,tot,c,pos,idx,zd;
int head1[N],head2[N],dfn[N],low[N],belong[N],Stack[N],vis[N];
int fa[N],son[N],top[N],dep[N],num[N],p[N],pr[18][N];
inline int sc()
{
    char c;
    int ret;
    if(c=getchar(),c==EOF) return 0;
    while(c!='-'&&(c<'0'||c>'9')) c=getchar();
    ret=(c=='-')?0:(c-'0');
    while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
    return ret;
}
struct node//线段树
{
    int l,r,sum;
} a[N<<2];
struct Edge//最终的图
{
    int to,next;
} e1[N*4],e2[N*4];
struct EDGE//原图
{
    int u,v;
    ll val;
} edge[N*4];
struct opera//操作数
{
    int op,u,v,ans;
    ll val;
} tc[N*4];
typedef pair<int,int> PII;
multiset <PII> M1;
map< ll,int>mp;
void init()
{
    zd=tot=c=idx=pos=0;
    memset(head1,-1,sizeof(head1));
    memset(head2,-1,sizeof(head2));
    memset(son,-1,sizeof(son));
    memset(vis,0,sizeof(vis));
    memset(dfn,0,sizeof(dfn));
    M1.clear();
}
void add(int u,int v,int f)//f为1表示最终的图,f为2表示终图缩点后的图
{
    if(f==1)
    {
        e1[tot].to=v,e1[tot].next=head1[u];
        head1[u]=tot++;
        e1[tot].to=u,e1[tot].next=head1[v];
        head1[v]=tot++;
    }
    else
    {
        e2[tot].to=v,e2[tot].next=head2[u];
        head2[u]=tot++;
        e2[tot].to=u,e2[tot].next=head2[v];
        head2[v]=tot++;
    }
}
void tarjan(int u,int pre)//求最终图的联通分量然后缩点
{
    int v;
    low[u]=dfn[u]=++idx;
    Stack[zd++]=u,vis[u]=1;
    for(int i=head1[u]; i+1; i=e1[i].next)
    {
        v=e1[i].to;
        if(v==pre) continue;
        if(!dfn[v])
        {
            tarjan(v,u);
            if(low[u]>low[v]) low[u]=low[v];
        }
        else if(vis[v]&&low[u]>dfn[v]) low[u]=dfn[v];
    }
    if(dfn[u]==low[u])
    {
        c++;
        do
        {
            v=Stack[--zd];
            vis[v]=0;
            belong[v]=c;
        }
        while(v!=u);
    }
}
void Tarjan()
{
    for(int i=1; i<=n; i++) if(!dfn[i]) tarjan(i,i);
    tot=0;
    mp.clear();
    //缩点
    for(int i=1; i<=n; i++)
    {
        for(int j=head1[i]; j+1; j=e1[j].next)
        {
            int v=e1[j].to;
            ll tmp1=ll(i)*INF+v;
            ll tmp2=ll(v)*INF+i;
            if(belong[i]!=belong[v]&&!mp[tmp1]&&!mp[tmp2])
            {
                add(belong[i],belong[v],2);//构造一颗树
                mp[tmp1]=mp[tmp2]=1;
            }
        }
    }
}
void dfs1(int u,int pre,int d)
{
    pr[0][u]=pre,dep[u]=d,fa[u]=pre,num[u]=1;
    for(int i=head2[u]; i+1; i=e2[i].next)
    {
        int v=e2[i].to;
        if(v==pre) continue;
        dfs1(v,u,d+1);
        num[u]+=num[v];
        if(son[u]==-1||num[v]>num[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int tp)
{
    top[u]=tp,p[u]=++pos;
    if(son[u]==-1) return ;
    dfs2(son[u],tp);
    for(int i=head2[u]; i+1; i=e2[i].next)
    {
        int v=e2[i].to;
        if(v!=son[u]&&v!=fa[u]) dfs2(v,v);
    }
}
void init_lca()//缩点后总共c个点
{
    pr[0][belong[1]]=-1;
    for(int k=0; k<16; k++)
        for(int i=1; i<=c; i++)
            if(pr[k][i]<0) pr[k+1][i]=-1;
            else pr[k+1][i]=pr[k][pr[k][i]];
}
int lca(int u,int v)
{
    if(dep[v]>dep[u]) swap(u,v);
    for(int k=0; k<16; k++)  if((dep[u]-dep[v])>>k&1) u=pr[k][u];
    if(u==v) return u;
    for(int i=15; i>=0; i--)
        if(pr[i][u]!=pr[i][v])
        {
            u=pr[i][u];
            v=pr[i][v];
        }
    return pr[0][u];
}
void pushdown(int k)
{
    if(a[k].l!=a[k].r&&a[k].sum==0)
        a[k*2].sum=a[k*2+1].sum=0;
}
void build(int l,int r,int k)
{
    a[k].l=l,a[k].r=r,a[k].sum=0;
    if(l==r)
    {
        a[k].sum=1;
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,2*k);
    build(mid+1,r,2*k+1);
    a[k].sum=a[k*2].sum+a[k*2+1].sum;
}
void update(int l,int r,int x,int k)//区间更新
{
    if(l<=a[k].l&&a[k].r<=r)
    {
        a[k].sum=x;
        return ;
    }
    pushdown(k);
    int mid=(a[k].l+a[k].r)/2;
    if(l<=mid) update(l,r,x,2*k);
    if(r>mid) update(l,r,x,2*k+1);
    a[k].sum=a[k*2].sum+a[k*2+1].sum;
}
int query(int l,int r,int k)
{
    if(a[k].l==l&&a[k].r==r) return a[k].sum;
    pushdown(k);
    int mid=(a[k].l+a[k].r)/2;
    if(r<=mid) return query(l,r,2*k);
    if(l>mid) return  query(l,r,2*k+1);
    return query(l,mid,2*k)+query(mid+1,r,2*k+1);
}
int op(int a,int b,int  f)//f=1表示加边,为2表示查询路径和
{
    if(a==b)  return 0;
    int ANS=0;
    int father=lca(a,b);
    ANS=-query(p[father],p[father],1);
    while(top[a]!=top[b])
    {
        if(dep[top[a]]>dep[top[b]]) swap(a,b);
        if(f==1) update(p[top[b]],p[b],0,1);
        else ANS+=query(p[top[b]],p[b],1);
        b=fa[top[b]];
    }
    if(dep[a]>dep[b]) swap(a,b);
    if(f==1)
    {
        update(p[a],p[b],0,1);
        update(p[father],p[father],-ANS,1);
    }
    else ANS+=query(p[a],p[b],1);
    return ANS;
}
int main()
{
    int  t;
    scanf("%d",&t);
    int t1=t;
    while(t--)
    {
        init();
        scanf("%d%d%d",&n,&m,&q);
        int from,to;
        for(int i = 1; i <= m; i++){
            scanf("%d%d", &from, &to);
            if(from > to) swap(from, to);
            M1.insert(make_pair(from,to));
        }
        for(int i = 1; i <= q; i++){
            scanf("%d%d%d", &tc[i].op, &tc[i].u, &tc[i].v);
            if(tc[i].u > tc[i].v) swap(tc[i].u, tc[i].v);
            if(tc[i].op == 1){
                multiset<PII>::iterator it = M1.find(make_pair(tc[i].u, tc[i].v));
                M1.erase(it);
            }
        }
        for(multiset<PII>::iterator it = M1.begin(); it != M1.end(); it++){
                add(it->first, it->second,1);
                add(it->second, it->first,1);
            }
        Tarjan();//O(n)
        dfs1(belong[1],belong[1],1);//O(n)
        dfs2(belong[1],belong[1]);//O(n)
        init_lca();
        build(1,c,1);//O(nlogn)
        printf("Case #%d:\n",t1-t);
        for(int i=q; i>=1; i--)
        {
            if(tc[i].op==1)//增加边
                op(belong[tc[i].u],belong[tc[i].v],1);
            else tc[i].ans=op(belong[tc[i].u],belong[tc[i].v],2);
        }
        for(int i=1; i<=q; i++) if(tc[i].op==2) printf("%d\n",tc[i].ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值