21.7.6 t3

tag:虚树,重链剖分,交互,构造,二分


首先预处理一下以 \(1\) 为根,每个点到根的 \(dis\),然后用一次操作可以求出任意两点的 \(lca\)\(lca=dep_x\oplus dep_y\oplus query(x,y)\)

可以考虑增量法,每次加入一个点,然后维护当前点集的虚树。

对于每次添加操作,首先对虚树重链剖分。

主要就是一个函数insert(chain,x),每次就调用insert(bel[1],x)

然后分为很多多多多多多多多多种情况。


首先求出链尾 \(lf\)\(x\)\(lca(y)\),对 \(y\) 分类讨论:

  • 如果 \(y=lf\),直接令 \(fa[x]=lf\)
  • 否则如果 \(y\) 在虚树上,则找到 \(x\) 属于 \(y\) 的哪个轻儿子,然后往下递归。
  • 否则二分出 \(y\) 在这条链上的位置,然后直接插进去。

对于第二种情况,还要分类讨论。

首先可以用一次询问求出 \(sum=\bigoplus_{v\in son_{y}} lca(x,v)\)

这个结果实际上是,若干个 \(y\) 和某个值 \(k\) 的异或和,因为 \(x\) 和其他轻儿子的 \(lca\) 一定是 \(y\)

于是对奇偶性简单分讨一下可以求出 \(k\)

\(v\) 为虚树上 \(y\)\(x\) 那一侧的儿子。(这个可以通过二分用 \(log\) 次操作求出)

(注意有一种情况是 \(x\) 这一侧的点都还没有加入虚树,这时直接把 \(x\) 接在 \(y\) 下面即可。)

对于 \(k\) 分为几种情况:

  • \(k=x\),说明 \(x\)\(v\) 的祖先,于是令 \(fa[x]=y,fa[v]=x\)
  • \(k=v\),递归调用 insert(bel[v],x),这里不用求出 \(v\),直接看 \(k\) 是否出现过就行,所以复杂度不是 \(log^2\)
  • \(k!=x,k!=v\),令 \(fa[k]=lca,fa[x]=fa[v]=k\)

总之细节很多,调用次数是 \(O(nlogn)\),复杂度为 \(O(n^2logn)\)

重链剖分是为了保证递归部分的复杂度。


代码照着上述过程写,唯一的细节可能就是二分了。。

多写几个函数就不太难写。

#include "hypercube.h"
#include<bits/stdc++.h>
using namespace std;

enum{
    MAXN = 3005
};

int n;

vector<int>to[MAXN];

int fa[MAXN], son[MAXN], leaf[MAXN], top[MAXN], sz[MAXN];
char vis[MAXN];
void dfs1(int x){
    sz[x] = 1; son[x] = 0;
    for(int v:to[x]){
        dfs1(v);
        sz[x] += sz[v];
        if(sz[v] > sz[son[x]]) son[x] = v;
    }
}

void dfs2(int x, int y){
    top[x] = y;
    if(son[x]) dfs2(son[x],y), leaf[x] = leaf[son[x]];
    else leaf[x] = x;
    for(int v:to[x]) if(v!=fa[x] and v!=son[x]) dfs2(v,v);
}

inline void rebuild(){
    for(int i=1; i<=n; i++) if(vis[i]) to[i].clear();
    for(int i=1; i<=n; i++) if(fa[i]) to[fa[i]].push_back(i);
    dfs1(1); dfs2(1,1);
}

int dep[MAXN];
inline int Query(int x, int y){
    static vector<int>tmp(1);
    tmp[0] = y; return query(x,tmp);
}

inline char check(int x, int pre, int y){
    static vector<int>tmp;
    tmp.clear();
    int res=0;
    for(int i=0; i<pre; i++) tmp.push_back(to[x][i]), res ^= dep[to[x][i]];
    res ^= query(y,tmp);
    if(pre&1) res ^= dep[y];
    if((res==0 and (~pre&1)) or (res==x and (pre&1))) return false;
    return true;
}

inline int Lca(int x, int y){return dep[x]^dep[y]^Query(x,y);}

void insert(int chaintop, int x){
    int lf = leaf[chaintop];
    int lca = (dep[lf]^dep[x]^Query(lf,x));
    if(lca==lf) return fa[x] = lf, void();
    // fprintf(stderr,"lca(%d %d) = %d\n",lf,x,lca);
    if(vis[lca]){
        int which = query(x,to[lca]);
        for(int v:to[lca]) which ^= dep[v];
        char odd = (int)to[lca].size()&1;
        if(odd) which ^= dep[x];
        if((which==0 and !odd) or (which==lca and odd)) return fa[x] = lca, void();
        if(!odd) which ^= lca;
        // fprintf(stderr,"which=%d\n",which);
        if(which!=x and vis[which]) return insert(which,x);

        int head=1, tail=to[lca].size();
        while(head<tail){
            int mid = head+tail >> 1;
            if(check(lca,mid,x)==false) head = mid+1;
            else tail = mid;
        }
        vis[which] = true;
        fa[which] = lca, fa[to[lca][head-1]] = which;
        if(which!=x) fa[x] = which;
        return;
    }

    if(x!=lca) fa[x] = lca, vis[lca] = true;
    static int chain[MAXN];
    int cnt=0;
    for(int i=lf; top[i]==chaintop; i=fa[i]) chain[++cnt] = i;
    int head=1, tail=cnt;
    while(head<tail){
        int mid = head+tail >> 1;
        if(Lca(chain[mid],lca)==lca) head = mid+1;
        else tail = mid;
    }
    fa[chain[head-1]] = lca;
    fa[lca] = chain[head];
}

void solve(int n, int Q, int data_type){
    for(int i=1; i<=n; i++) vis[i] = false, dep[i] = Query(1,i), fa[i] = 0; 
    ::n = n; vis[1] = true;
    for(int i=2; i<=n; i++) if(!vis[i]){
        rebuild();
        // fprintf(stderr,"insert %d\n",i);
        insert(1,i);
        vis[i] = true;
        // for(int j=1; j<=n; j++) if(vis[j]) fprintf(stderr,"(%d %d) ",j,fa[j]);cerr<<'\n';
    }
    for(int i=2; i<=n; i++) report(i,fa[i]);
}
posted @ 2021-07-06 16:53  oisdoaiu  阅读(51)  评论(0编辑  收藏  举报