P3950 部落冲突

此题用树链剖分是大材小用了,因为它可以处理 \(p,q\) 不相邻的情况。

简化一下题意:

  • \(Q\ p\ q\) 代表查询树上最短路径 \(p\ q\) (也就是 \(p->lca(p,q)->q\))之间有没有边的为黑色。有黑色就输出 \(No\),全部都是白色输出 \(Yes\)

  • \(C\ p\ q\) 代表让 \(p,q\) 的连边变为黑色。(\(p,q\) 相邻)

  • \(U\ p\) 代表让第 \(p\) 次修改的连边变为白色。

这都是一些树上的操作,乍一看可以用树剖直接搞。但是因为这里是 进行操作,所以我们可以 以点代边 ,把边看做点。每一次查询的时候 \(lca(p,q)\) 是不能算的。

修改的话就简单了,因为是相邻的,所以我们可以直接把 \(dep\) 比较深的那个点记录 (用来结束战争) 并在线段树上单点 \(+1\)。对于每一次结束战争我们就把记录的那个点在线段树上 \(-1\)

如果 \(p,q\) 不相邻,我们可以加入树剖的修改操作,但是线段树的修改操作就必须是 区间赋值,需要打一个精妙的标记。

时间复杂度: \(O(n\log^2 n)\),常数巨大。

// luogu-judger-enable-o2

Uses math;

var
    recf,cnt,size,dfn,dep,top,father,son:array[-1..3100000] of longint;
    next,reach:array[-1..8500000] of longint;
    left,right:array[-1..8500000] of longint;
    ques:array[-1..3100000] of longint;
    tree:array[-1..15000000] of int64; // 线段树的空间要开大
    i,n,m,l,r,dfnum,tot,root,tail:longint;
    order:char;

procedure swap(var a,b:longint);var t:longint; begin t:=a; a:=b; b:=t; end;

procedure add(l,r:longint);
begin
    inc(tot);
    reach[tot]:=r;
    next[tot]:=cnt[l];
    cnt[l]:=tot;
end;

procedure Dfs_1(x:longint);
var i:longint;
begin
    size[x]:=1; i:=cnt[x]; size[0]:=-maxlongint div 843;
    while i<>-1 do
    begin
        if dep[reach[i]]=0 then
        begin
            dep[reach[i]]:=dep[x]+1;
            father[reach[i]]:=x;
            Dfs_1(reach[i]); inc(size[x],size[reach[i]]);
            if size[reach[i]]>size[son[x]] then son[x]:=reach[i];
        end;
        i:=next[i];
    end;
end;

procedure Dfs_2(x,centre:longint);
var i:longint;
begin
    inc(dfnum); dfn[x]:=dfnum; recf[dfnum]:=x; top[x]:=centre;
    if son[x]=0 then exit; Dfs_2(son[x],centre);
    i:=cnt[x];
    while i<>-1 do
    begin
        if (reach[i]<>father[x])and(reach[i]<>son[x]) then Dfs_2(reach[i],reach[i]);
        i:=next[i];
    end;
end;

procedure Build(k,l,r:longint);
var mid:longint;
begin
    left[k]:=l; right[k]:=r;
    if (l=r) then exit;
    mid:=(l+r) >> 1;
    Build(k << 1,l,mid); Build(k << 1+1,mid+1,r);
end;

procedure Change(k,x,modify:longint);
var mid:longint;
begin
    if left[k]=right[k] then begin inc(tree[k],modify); exit; end;
    mid:=(left[k]+right[k]) >> 1;
    if x<=mid then Change(k << 1,x,modify) else Change(k << 1+1,x,modify);
    tree[k]:=tree[k << 1]+tree[k << 1+1];
end;

function Query(k,x,y:longint):int64;
var mid:longint;
begin
    Query:=0;
    if x>y then exit(0);
    if (x<=left[k])and(right[k]<=y) then exit(tree[k]);
    mid:=(left[k]+right[k]) >> 1;
    if mid>=y then inc(Query,Query(k << 1,x,y)) else
    if mid<x then inc(Query,Query(k << 1+1,x,y)) else
    inc(Query,Query(k << 1,x,mid)+Query(k << 1+1,mid+1,y));
end;

function Refer(x,y:longint):string; // 查询
var tmp:longint;
begin
    while top[x]<>top[y] do
    begin
        if dep[top[x]]<dep[top[y]] then swap(x,y);
        tmp:=Query(1,dfn[top[x]],dfn[x]); if tmp>=1 then exit('No'); // 如果这一段有一个黑色,那么 No
        x:=father[top[x]];
    end;
    if dep[x]>dep[y] then swap(x,y);
    tmp:=Query(1,dfn[x]+1,dfn[y]); if tmp>=1 then exit('No'); // dfn[x]+1 是因为 lca(x,y) 不能加
    exit('Yes'); 
end;

begin
    filldword(cnt,sizeof(cnt) div 4,maxlongint*2+1);
    readln(n,m); root:=1; recf[-1]:=-1;
    for i:=1 to n-1 do begin readln(l,r); add(l,r); add(r,l); end;
    dep[root]:=1; father[root]:=1;
    Dfs_1(root); Dfs_2(root,root); Build(1,1,n);
    for i:=1 to m do
    begin
        read(order);
        if order='C' then begin readln(l,r); if dep[l]<dep[r] then l:=r; Change(1,dfn[l],1); inc(tail); ques[tail]:=l; end;
        if order='Q' then begin readln(l,r); writeln(Refer(l,r));end;
        if order='U' then begin readln(l); Change(1,dfn[ques[l]],-1);  end;
    end;
end.
posted @ 2018-10-20 15:53  _ARFA  阅读(118)  评论(0编辑  收藏  举报