zoukankan      html  css  js  c++  java
  • 题解 洛谷 P5443 【[APIO2019]桥梁】

    考虑若只有查询操作,那么就可以构造(Kruskal)重构树,然后在线询问了,也可以更简单的把询问离线,把询问和边都按权值从大到小排序,然后双指针依次加入对于当前询问合法的边,用并查集维护每个点的答案即可。

    现在加上修改操作,考虑可以对所有操作分块,对块内所有询问操作排序,和之前的离线做法一样,用双指针依次加边。

    但是有些边会在块内这些操作中涉及修改,那么对于每个询问,暴力扫一遍块内的修改操作,若一个修改操作在当前询问的时间点之前,那么就执行对应边的修改,执行完所有能够执行的修改后,再考虑这些涉及修改的边,加入合法的。

    当指针扫到下一个询问时,因为询问已经不再是按时间顺序,所以有可能上一个询问执行的一些修改在当前询问不能生效,所以用可撤销并查集把除了不涉及修改的边都撤销掉,然后就和之前处理询问的过程一样了。

    设块大小为(S),对于一个块,需要对边排序和处理询问,处理询问时需要枚举所有的修改和用可撤销并查集来维护,那么处理一个块的复杂度为(O(mlog m + S^2 log n)),总共(frac{q}{S})个块,所以总复杂度就为(O(frac{q mlog m}{S}+ qS log n)),块大小取(sqrt{mlog m})时会比较快。

    (code:)

    #include<bits/stdc++.h>
    #define maxn 200010
    using namespace std;
    template<typename T> inline void read(T &x)
    {
        x=0;char c=getchar();bool flag=false;
        while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        if(flag)x=-x;
    }
    int n,m,S,qu,tot,top,cnt1,cnt2;
    int ans[maxn],fa[maxn],siz[maxn],bel[maxn],val[maxn];
    bool vis[maxn];
    struct edge
    {
        int x,y,v,id;
    }e[maxn];
    bool cmp1(const edge &a,const edge &b)
    {
        return a.v>b.v;
    }
    struct query
    {
        int x,v,id,opt;
    }q[maxn],q1[maxn],q2[maxn];
    bool cmp2(const query &a,const query &b)
    {
        return a.v>b.v;
    }
    struct node
    {
        int x,y;
    }st[maxn];
    int find(int x)
    {
        return fa[x]==x?x:find(fa[x]);
    }
    void merge(int x,int y)
    {
        int rx=find(x),ry=find(y);
        if(rx==ry) return;
        if(siz[rx]<siz[ry]) swap(rx,ry);
        st[++top]=(node){rx,ry};
        fa[ry]=rx,siz[rx]+=siz[ry];
    }
    void del(int id)
    {
        int x=st[id].x,y=st[id].y;
        fa[y]=y,siz[x]-=siz[y];
    }
    void init()
    {
        top=cnt1=cnt2=0,sort(e+1,e+m+1,cmp1);
        for(int i=1;i<=n;++i) fa[i]=i,siz[i]=1;
        for(int i=1;i<=m;++i) val[i]=vis[i]=0,bel[e[i].id]=i;
        for(int i=1;i<=tot;++i)
        {
            if(q[i].opt==1) q1[++cnt1]=q[i],vis[q[i].x]=true;
            else q2[++cnt2]=q[i];
        }
        sort(q2+1,q2+cnt2+1,cmp2);
    }
    void work()
    {
        init();
        int now,pos=1;
        for(int i=1;i<=cnt2;++i)
        {
            int x=q2[i].x,v=q2[i].v,id=q2[i].id;
            while(e[pos].v>=v)
            {
                if(!vis[e[pos].id])
                    merge(e[pos].x,e[pos].y);
                pos++;
            }
            now=top;
            for(int j=1;j<=cnt1;++j)
                val[q1[j].x]=e[bel[q1[j].x]].v;
            for(int j=1;j<=cnt1;++j)
                if(q1[j].id<id)
                    val[q1[j].x]=q1[j].v;
            for(int j=1;j<=cnt1;++j)
                if(val[q1[j].x]>=v)
                    merge(e[bel[q1[j].x]].x,e[bel[q1[j].x]].y);
            ans[id]=siz[find(x)];
            while(top>now) del(top--);
        }
        for(int i=1;i<=cnt1;++i) e[bel[q1[i].x]].v=q1[i].v;
    }
    int main()
    {
        read(n),read(m),S=sqrt(m*log2(m));
        for(int i=1;i<=m;++i)
            read(e[i].x),read(e[i].y),read(e[i].v),e[i].id=i;
        read(qu);
        for(int i=1;i<=qu;++i)
        {
            q[++tot].id=i,read(q[tot].opt);
            read(q[tot].x),read(q[tot].v);
            if(tot==S) work(),tot=0;
        }
        if(tot) work();
        for(int i=1;i<=qu;++i)
            if(ans[i])
                printf("%d
    ",ans[i]);
        return 0;
    }
    
  • 相关阅读:
    《metasploit渗透测试魔鬼训练营》靶机演练之第五章实战案例KingView 6.53版本CVE-2011-0406漏洞
    《metasploit渗透测试魔鬼训练营》学习笔记第五章--网络服务渗透攻击
    《metasploit渗透测试魔鬼训练营》靶机演练之第五章实战案例Oracle数据库
    《metasploit渗透测试魔鬼训练营》学习笔记第四章—web应用渗透
    《metasploit渗透测试魔鬼训练营》学习笔记第三章----情报搜集
    Kali-linux Arpspoof工具
    Kali-linux攻击路由器
    Kali-linux在树莓派上破解无线网络
    Kali-linux使用Easy-Creds工具攻击无线网络
    20155225 2016-2017-2 《Java程序设计》第五周学习总结
  • 原文地址:https://www.cnblogs.com/lhm-/p/12877297.html
Copyright © 2011-2022 走看看