JZOJ 4739 【雅礼联考GDOI2017模拟9.2】Ztxz16学图论

Ztxz16学图论

题目大意

给定 N 个点,M条无向边, Q 个询问,每个询问给定L, R ,问连上第L~ R 条边后,图中有多少联通块。

数据范围

N, M ,Q<= 200000 L <=R

题解

做到这题的时候,我真的是不知所措,不过后面还是想到了一种解法。然而,这题提供的题解,竟是LCT(动态树),表示我这种蒟蒻实在是不会这种高级的做法了。
我们看一下题目,只有询问操作,没有修改操作,可以离线处理,这让我们不禁想到了一种奥妙重重的算法——莫队算法。

然而我们会发现一个残酷的现实,在转移的同时我们需要维护一个并查集,加边容易维护答案,可是删边时难以维护并查集和答案啊!
那这样子是否意味着莫队算法就不能做了呢? NO !
注意现在,我们只能使用加边操作,不能使用删边操作,用并查集就可以实现,但如何在不能使用删边操作下维护答案呢?
这个问题是可以解决的。

假设当前的一连串询问左边界L所在块的编号都相同(这里的编号指分块后的编号),此时右坐标 R 是递增,当前这一块的右边界为R,因为右坐标是不断递增,于是我们维护多一个并查集 K ,用来保存R~ R 的信息,这个容易维护,因为维护时也就只有加边操作了。
然后对于每次询问,我们现时维护并查集K R ~ R ),然后以K为基础,转移到我们要维护的那个并查集 U L~ R )。具体实现如下:

function getfather(o:longint):longint;
begin
    if 并查集U[o]尚未从并查集K获取信息 then
    begin
        U[o]:=K[o];
        将点o的状态设为已获取信息;
    end;
    if U[o]=o then exit(o);//如果是并查集的根,则退出并返回函数值
    U[o]:=getfather(U[o]);
    exit(U[o]);
end;

像这样维护并查集并同时维护答案即可。
我们分析一下时间复杂度,每一个块的R最多变成 N ,一共有N个块,时间复杂度为( N N)。
再看一下 L ,每次询问L顶多移动 N ,时间复杂度为( M N)。
总的来说,时间复杂度是 1.5 次的,这十分优秀。

var
    f1,f2,ans,ph:array[0..200000] of longint;
    cqy,ff,be,en,n,m,q,i,j,k,l,xd,o,p,u:longint;
    xw:array[0..200000,1..4] of longint;
    bj:array[0..200000,1..2] of longint;
function min(a,b:int64):int64;
    begin
        if a<b then exit(a)
        else exit(b);
    end;
procedure sjzl;
    var
        i,k:longint;
    begin
        randomize;
        for i:=1 to q div 2 do
        begin
            k:=q div 2+1+random(q div 2);
            xw[0]:=xw[i];
            xw[i]:=xw[k];
            xw[k]:=xw[0];
        end;
    end;
procedure qsort(l,r:longint);
    var
        i,j,m,mm:longint;
    begin
        i:=l;
        j:=r;
        m:=xw[(l+r) div 2,3];
        mm:=xw[(l+r) div 2,2];
        repeat
            while (xw[i,3]<m) or (xw[i,3]=m) and (xw[i,2]<mm) do inc(i);
            while (xw[j,3]>m) or (xw[j,3]=m) and (xw[j,2]>mm) do dec(j);
            if i<=j then
            begin
                xw[0]:=xw[i];
                xw[i]:=XW[j];
                XW[j]:=xw[0];
                inc(i);
                dec(j);
            end;
        until i>j;
        if l<j then qsort(l,j);
        if i<r then qsort(i,r);
    end;
function find1(o:longint):longint;
    begin
        if ph[o]<>xd then
        begin
            f1[o]:=f2[o];
            ph[o]:=xd;
        end;
        if f1[o]=o then exit(O);
        f1[o]:=find1(f1[o]);
        exit(f1[o]);
    end;
function find2(o:longint):longint;
    begin
        if f2[o]=o then exit(o);
        f2[o]:=find2(f2[o]);
        exit(f2[o]);
    end;
begin
    readln(n,m,q);
    for i:=1 to m do
    readln(bj[i,1],bj[i,2]);
    p:=trunc(sqrt(n));
    for i:=1 to q do
    begin
        read(xw[i,1],xw[i,2]);
        xw[i,4]:=i;
        xw[i,3]:=xw[i,1] div p;
        if xw[i,1] mod p>0 then inc(xw[i,3]);
    end;
    sjzl;
    qsort(1,q);
    cqy:=0;
    ff:=n;
    for i:=1 to q do
    begin
        if cqy<>xw[i,3] then
        begin
            cqy:=xw[i,3];
            be:=min(n,p*xw[i,3]);
            en:=be-1;
            ff:=n;
            for j:=1 to n do
            begin
                f1[j]:=j;
                f2[j]:=j;
            end;
        end;
        o:=ff;
        if xw[i,2]<be then
        begin
            inc(xd);
            for j:=xw[i,1] to xw[i,2] do
            if find1(bj[j,1])<>find1(bj[j,2]) then
            begin
                f1[f1[bj[j,1]]]:=f1[bj[j,2]];
                dec(o);
            end;
            ans[xw[i,4]]:=o;
        end
        else
        begin
            for j:=en+1 to xw[i,2] do
            if find2(bj[j,1])<>find2(bj[j,2]) then
            begin
                f2[f2[bj[j,1]]]:=f2[bj[j,2]];
                dec(ff);
            end;
            en:=xw[i,2];
            inc(xd);
            o:=ff;
            for j:=xw[i,1] to be-1 do
            if find1(bj[j,1])<>find1(bj[j,2]) then
            begin
                f1[f1[bj[j,1]]]:=f1[bj[j,2]];
                dec(o);
            end;
            ans[xw[i,4]]:=o;
        end;
    end;
    for i:=1 to q do
    writeln(ans[i]);
end.
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值