zoukankan      html  css  js  c++  java
  • [agc002D]Stamp Rally-[并查集+整体二分]

    Description

    题目大意:给你一个n个点m个条边构成的简单无向连通图,有Q组询问,每次询问从两个点x,y走出两条路径,使这两条路径覆盖z个点,求得一种方案使得路径上经过的边的最大编号最小。n,m,q105

    Solution

    这个题我们可以考虑二分。

    对于询问(x,y,z),二分k为答案,暴力做法是加入编号为1-k的边,判断x,y是否在同一个联通块内,如果是,则可覆盖点数为sz[x所在联通块点数](记为sz[x]),如果不是,则可覆盖点数为sz[x]+sz[y]。

    然后看到询问特别多。em我们就可以考虑整体二分。(二分答案k,判断有多少个询问的答案小于等于它)。至于判断x,y是否处于同一联通块和求联通块大小,可以采用并查集。并查集可以按秩合并,这样撤销比较方便。(或者按size合并也是可以的)。

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    int n,m,Q,x[200010],y[200010],xx,yy,z;
    struct ASK{int x,y,v,id;
    }q[200010],re[200010];
    int ans[200010],t[2][200010];ASK a[200010],b[200010];
    int sz[200010],fa[200010];
    int get_fa(int x){return fa[x]==x?x:get_fa(fa[x]);}
    void solve(int l,int r,int ansl,int ansr)
    {
        int _x,_y;
        if (ansl==ansr)
        {
            for (int i=l;i<=r;i++) ans[q[i].id]=ansl;
            _x=get_fa(x[ansl]);_y=get_fa(y[ansl]);
            if (_x!=_y)
            {
                if (sz[_x]>sz[_y]) swap(_x,_y);
                fa[_x]=_y;sz[_y]+=sz[_x];
            }
            return;
        }
        int ansmid=ansl+ansr>>1,js1=0,js2=0,top=0;
        for (int i=ansl;i<=ansmid;i++)
        {
            _x=get_fa(x[i]);_y=get_fa(y[i]);
            if (_x!=_y)
            {
                if (sz[_x]>sz[_y]) swap(_x,_y);
                fa[_x]=_y;sz[_y]+=sz[_x];
                t[0][++top]=_x;t[1][top]=_y;
            }
        }
        for (int i=l;i<=r;i++)
        {
            _x=get_fa(q[i].x);_y=get_fa(q[i].y);
            if ((_y!=_x&&sz[_x]+sz[_y]>=q[i].v)||(_y==_x&&sz[_x]>=q[i].v)) a[++js1]=q[i];
            else b[++js2]=q[i];
        }
        for (;top;top--)
        {
            fa[t[0][top]]=t[0][top];sz[t[1][top]]-=sz[t[0][top]];
        }
        for (int i=1;i<=js1;i++) q[i+l-1]=a[i];
        for (int i=1;i<=js2;i++) q[i+l+js1-1]=b[i];
        solve(l,l+js1-1,ansl,ansmid);
        solve(l+js1,r,ansmid+1,ansr);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++)    scanf("%d%d",&x[i],&y[i]);
        scanf("%d",&Q);
        for (int i=1;i<=Q;i++)
        {
            scanf("%d%d%d",&xx,&yy,&z);
            q[i]=ASK{xx,yy,z,i};
        }
        for (int i=1;i<=n;i++) fa[i]=i,sz[i]=1;
        solve(1,Q,1,m);
        for (int i=1;i<=Q;i++) printf("%d
    ",ans[i]);
    }
  • 相关阅读:
    or具体点vc
    异常 中断 实现
    int 0x80 系统调用实现
    方便查看 linux/kernel/sched.c
    第五周课堂笔记1th
    第四周课堂笔记4th
    第四周课堂笔记3th
    第四周课堂笔记2th
    第四周课堂笔记1th
    第三周课堂笔记4thand5th
  • 原文地址:https://www.cnblogs.com/coco-night/p/9481868.html
Copyright © 2011-2022 走看看