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;
    }
  • 相关阅读:
    2级联动下拉列表写法
    select选中获取索引三种写法
    判断设备-安卓|苹果|微信
    限制输入字符个数的jq插件
    面试题:1.清空字符串前后的空格;2.找出出现最多的字符
    css3玩转各种效果【资源】
    利用jquery.touchSwipe.js实现的移动滑屏效果。
    【leetcode刷题笔记】Letter Combinations of a Phone Number
    【leetcode刷题笔记】Linked List Cycle
    【leetcode刷题笔记】Length of Last Word
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11361596.html
Copyright © 2011-2022 走看看