zoukankan      html  css  js  c++  java
  • P5443 [APIO2019]桥梁

    传送门

    子任务 $4$ 告诉我们可以离线搞带权并查集

    从大到小枚举询问,从大到小连边

    如果没有修改操作就可以过了

    但是有修改,考虑最暴力的暴力,搞可撤销并查集

    同样先离线,从大到小处理询问时,按原边权从大到小枚举到一条边时,如果他一直都没有修改,那么直接加入并查集

    如果有修改那先不要加,枚举所有修改看看当前时间它的边权,然后如果它的边权大于等于询问权值,才加入并查集

    最后还得撤销有修改的边,因为它们修改后的边权不满足从大到小

    这样复杂度显然很高,因为我们每次都要撤销一堆操作,还要枚举所有修改

    考虑分块,按操作数量分块

    对于每个块,时间在它之前的所有操作肯定已经做完了,那么枚举修改时就少了很多枚举

    同理撤销的时候也只要撤销块内的操作,每个块做完直接把相应边权更改重新排序(归并排序可以做得比较快,代码里用 $sort$ )

    然后复杂度就很可行了

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<vector>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=5e5+7,SIZE=1024;//块大小自己试几次
    int n,m,T;
    struct edge{//
        int u,v,w,id;
        edge (int a=0,int b=0,int c=0,int d=0) { u=a,v=b,w=c,id=d; }
        inline bool operator < (const edge &tmp) const {
            return w>tmp.w;
        }
    }E[N];
    struct dat{//操作
        int x,w,id;
        dat (int a=0,int b=0,int c=0) { x=a,w=b,id=c; }
        inline bool operator < (const dat &tmp) const {
            return w>tmp.w;
        }
    };
    vector <dat> Q,C;//Q存询问,C存操作
    int fa[N],sz[N],st[N],Top;//待撤销并查集,st维护操作序列
    int find(int x) { return x==fa[x] ? x : find(fa[x]); }
    inline void merge(int x,int y)//启发式合并
    {
        int u=find(x),v=find(y); if(u==v) return;
        if(sz[u]<sz[v]) swap(u,v);
        fa[v]=u; sz[u]+=sz[v]; st[++Top]=v;
    }
    int ans[N],mx[N],id[N];//mx[i]是当前编号为i的边的权值,id[i]是编号为i的边当前的位置
    void solve()
    {
        sort(E+1,E+m+1);//每次都sort
        for(int i=1;i<=m;i++) mx[i]=0,id[ E[i].id ]=i; Top=0;
        for(int i=1;i<=n;i++) sz[i]=1,fa[i]=i;
        for(dat x : C) mx[x.x]=-1;//初始化,有修改的边先赋成-1
        sort(Q.begin(),Q.end()); int j=1,las,p;//把询问排序
        for(dat q : Q)//从大到小枚举询问
        {
            for(;E[j].w>=q.w;j++) if(!mx[ E[j].id ]) merge(E[j].u,E[j].v);//如果此边没有修改可以直接加入并查集
            las=Top;
            for(dat x : C) mx[x.x]=E[ id[x.x] ].w;//一开始先赋成原本值
            for(dat x : C) if(x.id<q.id) mx[x.x]=x.w;//有修改且时间比询问早就修改
            //这时更改时间在后面的边的mx也更新好了
            for(dat x : C) if(mx[x.x]>=q.w) merge(E[ id[x.x] ].u,E[ id[x.x] ].v);//如果边权较大才加入并查集
            ans[q.id]=sz[ find(q.x) ];//更新答案
            while(Top>las) p=st[Top--],sz[fa[p]]-=sz[p],fa[p]=p;//撤回
        }
        for(dat x : C) E[ id[x.x] ].w=x.w;//一个块搞完把相应边权更改改
        C.clear(); Q.clear();//记得清空
    }
    int main()
    {
        n=read(),m=read();
        for(int i=1;i<=m;i++) E[i].u=read(),E[i].v=read(),E[i].w=read(),E[i].id=i;
        T=read(); int opt,cnt=0; dat a;
        for(int i=1;i<=T;i++)
        {
            opt=read(); a.x=read(); a.w=read(); a.id=i;
            opt==1 ? C.push_back(a) : Q.push_back(a);
            cnt++; if(cnt==SIZE) solve(),cnt=0;
        }
        if(cnt) solve();
        for(int i=1;i<=T;i++) if(ans[i]) printf("%d
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    ArrayList removeRange方法分析
    LinkedHashMap源码分析(基于JDK1.6)
    LinkedList原码分析(基于JDK1.6)
    TreeMap源码分析——深入分析(基于JDK1.6)
    51NOD 2072 装箱问题 背包问题 01 背包 DP 动态规划
    51 NOD 1049 最大子段和 动态规划 模板 板子 DP
    51NOD 1006 最长公共子序列 Lcs 动态规划 DP 模板题 板子
    8月20日 训练日记
    CodeForces
    CodeForces
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11361596.html
Copyright © 2011-2022 走看看