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]);
}