ACM-ICPC北京赛区2017网络同步赛C

http://hihocoder.com/contest/icpcbeijing2017/problem/3

带撤销的并查集+莫队

口胡一波复杂度,暂且认为并查集的合并和撤销都是O(1)的

考虑离线询问,莫队处理,对于一个左右都在块内的询问直接暴力求解,求解一次就是sqrt(n)的

然后开始枚举左端点所在的块,首先对右端点排序,块内右端点递增,此时的询问都是这样的,左端点在[a1,a2]右端点在[a2+1,n],我们考虑这样一条边,u,v,如果uv都在[a2+1,n]这样的边不用撤销,因为后面一定会用到,对于u在[a1,a2]的边,这是需要撤销的,因此我们每次维护[a2+1,lastr]lastr是上一次询问的r 下一次的询问r比上一次大,然后再暴力维护左边(要撤销的边)。这样的复杂度这样算,对于一条边,被枚举只有2种情况,此时枚举的左分块包含左断点,这样被枚举的次数就是左分块所在的询问个数,第二种就是枚举的左分块不包含左端点,这样最多被枚举1次,均摊每条边被枚举的次数就是sqrt(m)(口胡);

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
#include <map>
#include <cmath>
using namespace std;
typedef long long int lll;
namespace Solve {
    lll n,m,q;
    lll ans;
    const lll maxn = 55009;
    lll fa[maxn],si[maxn];
    lll lef[maxn*2],rig[maxn*2];
    lll anss[maxn*2];
    vector<lll> modui[1009];
    lll block;
    stack< pair<lll *,lll > > stak;
    vector<lll> vv1[maxn];
    void init(){
        ans = 0;
        for(lll i=0;i<maxn;i++){
            fa[i] = i;
            si[i]= 1;
            vv1[i].clear();
        }
        for(lll i=0;i<1009;i++){
            modui[i].clear();
        }
        while (!stak.empty()) {
            stak.pop();
        }
        ans = 0;
        block = 0;
    }
    lll find(lll x){
        while (fa[x]!=x) {
            x = fa[x];
        }
        return x;
    }
    void uni(lll u,lll v,bool on){
        u = find(u);
        v = find(v);
        if(u==v) return ;
        if(si[u]<si[v]) swap(u,v);
        if(on) stak.push({&fa[v],fa[v]});
        fa[v] = u;
        if(on) stak.push({&ans,ans});
        ans-=si[u]*(si[u]-1)/2;
        ans-=si[v]*(si[v]-1)/2;
        if(on) stak.push({&si[u],si[u]});
        si[u]+=si[v];
        ans+=si[u]*(si[u]-1)/2;
    }
    void back(){
        while (!stak.empty()) {
            *stak.top().first = stak.top().second;
            stak.pop();
        }
    }
    void solve(){
        init();
        scanf("%lld%lld%lld",&n,&m,&q);
        block = sqrt(n);
        for(lll i=1;i<=m;i++){
            lll u,v;
            scanf("%lld%lld",&u,&v);
            vv1[u].push_back(v);
            vv1[v].push_back(u);
        }
        for(lll i=1;i<=q;i++){
            scanf("%lld%lld",&lef[i],&rig[i]);
            lll lb = (lef[i]-1)/block,rb =(rig[i]-1)/block;
            if(lb==rb){
                lll tmp = lef[i];
                while (tmp<=rig[i]) {
                    for(lll v : vv1[tmp])if(v<=rig[i]&& v>=lef[i]){
                        uni(tmp, v, true);
                    }
                    tmp++;
                }
                anss[i] = ans;
                back();
            }else{
                modui[lb].push_back(i);
            }
        }
        for(lll i=0;(i+1)*block<=n;i++){
            sort(modui[i].begin(), modui[i].end(), [](lll a,lll b){ return rig[a]<rig[b];});
            ans = 0;
            while (!stak.empty()) {
                stak.pop();
            }
            for(lll i=1;i<=n;i++){
                fa[i]= i;
                si[i] = 1;
            }
            lll tl = (i+1)*block;
            lll tr = tl-1;
            for(lll j :modui[i]){
                while (tr<rig[j]) {
                    tr++;
                    for(lll v:vv1[tr]) if(v<=tr && v>=tl){
                        uni(tr, v, false);
                    }
                }
                while (tl>lef[j]) {
                    tl--;
                    for(lll v:vv1[tl]) if(v<=tr && v>=tl){
                        uni(tl, v, true);
                    }
                }
                anss[j] = ans;
                back();
                tl = (i+1)*block;
            }
            
        }
        for(lll i=1;i<=q;i++){
            printf("%lld\n",anss[i]);
        }
    }
}
int main(int argc, const char * argv[]) {
    lll T;
    scanf("%lld",&T);
    while (T--) {
        Solve::solve();
    }
    return 0;
}

  

 
 

猜你喜欢

转载自www.cnblogs.com/tjucxz/p/8858481.html