【题解】 集合论 互测题 分块+欧拉序+压位
作者:互联网
Editorial
此处认为 \(\omega = 64\)。\(n\) 与值域同数量级。
这题有个显然的树上待修莫队,但感觉很屑,复杂度也不是很好(\(O(n^{\frac{5}{3}})\)),就没打算写。
于是想啊想啊,想了两个小时,搞出来一个复杂度更优的做法:
考虑先对原树跑出来欧拉序,把欧拉序分块,设块大小 \(B\)。每隔 \(B\) 个预处理它到根节点的异或 bitset。预处理复杂度 \(O(\frac{n^2}{B})\)。
这样子,我们就可以在 \(O(\frac{n}{\omega}+B)\) 的时间得到任何点到根的异或 bitset。
显然,得到了 bitset 之后就可以 \(O(\frac{n}{\omega})\) 得到答案。
修改操作直接判断修改的点是否是某个点的祖先,是,就需要修改 bitset。复杂度 \(O(qB)\)。
复杂度 \(O(\frac{n^2}{B}+\frac{nq}{\omega}+qB)\),当 \(B=\sqrt \frac{n^2}{q}\) 取到最优 \(O(n\sqrt{q}+\frac{nq}{\omega})\)。
Code
我由于是考场代码,没有写欧拉序分块,而是直接 DFS 序分块,每 \(\sqrt{n}\) 个结点求一次。
查询是向上跳到第一个预处理过的结点,根据构造可以卡到 \(O(n^{\frac{3}{4}})\) 的跳双亲次数。
长链剖分后这个查询的复杂度应该是 \(O(\frac{n}{\omega}+n^{\frac{3}{4}})\)。
#include <bits/stdc++.h>
#define debug(...) fprintf(stderr ,__VA_ARGS__)
#define __FILE(x)\
freopen(#x".in" ,"r" ,stdin);\
freopen(#x".out" ,"w" ,stdout)
#define LL long long
#define ULL unsigned long long
const int MX = 1e5 + 23;
using namespace std;
int read(){
char k = getchar(); int x = 0;
while(k < '0' || k > '9') k = getchar();
while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
return x;
}
const int BSZ = 160;
int n ,m;
ULL f[720][MX / 64 + 10];
int head[MX] ,tot = 1;
struct edge{
int node ,next;
}h[MX << 1];
void addedge(int u ,int v ,int flg = 1){
h[++tot] = (edge){v ,head[u]} ,head[u] = tot;
if(flg) addedge(v ,u ,false);
}
namespace FUCKLCA{
int ecnt ,id[MX] ,seq[18][MX << 1] ,dfn ,antid[MX] ,where[MX];
int lg2[MX << 1];
void dfs(int x ,int f){
id[x] = ++dfn;
antid[dfn] = x;
seq[0][++ecnt] = id[x];
where[x] = ecnt;
for(int i = head[x] ,d ; i ; i = h[i].next){
if((d = h[i].node) == f) continue;
dfs(d ,x);
seq[0][++ecnt] = id[x];
}
}
int LCA(int x ,int y){
x = where[x] ,y = where[y];
if(x > y) std::swap(x ,y);
int len = lg2[y - x + 1];
return antid[std::min(seq[len][x] ,seq[len][y - (1 << len) + 1])];
}
void main(){
dfs(1 ,0);
lg2[0] = -1;
for(int i = 1 ; i < MX << 1 ; ++i)
lg2[i] = lg2[i - 1] + (i == (i & -i));
for(int i = 1 ; i < 18 ; ++i)
for(int j = 1 ; j + (1 << i) - 1 <= ecnt ; ++j)
seq[i][j] = std::min(seq[i - 1][j] ,seq[i - 1][j + (1 << (i - 1))]);
}
}using FUCKLCA::LCA;
int dep[MX] ,hson[MX] ,fa[MX] ,sz[MX];
void dfs1(int x ,int f ,int dp){
fa[x] = f ,dep[x] = dp ,sz[x] = 1;
for(int i = head[x] ,d ; i ; i = h[i].next){
if((d = h[i].node) == f) continue;
dfs1(d ,x ,dp + 1) ,sz[x] += sz[d];
if(dep[d] > dep[hson[x]]) hson[x] = d;
}
}
ULL cur[MX / 64 + 10];
void obitset(ULL *A ,int len){
for(int i = 1 ; i <= len ; ++i){
if((A[i >> 6] >> (i & 63)) & 1){
printf("%d," ,i);
}
// printf("%llu" ,(A[i >> 6] >> (i & 63)) & 1);
if(i == len) printf("end\n");
}
}
int a[MX];
int dfn ,setup[MX] ,id[MX] ,antid[MX] ,bid[MX] ,antibid[MX];
void dfs2(int x){
cur[a[x] >> 6] ^= 1ull << (a[x] & 63);
// printf("%d: " ,x); obitset(cur ,30000);
id[x] = ++dfn;
bid[x] = dfn / BSZ;
antid[dfn] = x;
if(dfn % BSZ == 0){
antibid[dfn / BSZ] = x;
setup[x] = true;
for(int j = 0 ; j < MX / 64 + 1 ; ++j){
f[bid[x]][j] = cur[j];
}
}
if(hson[x]) dfs2(hson[x]);
for(int i = head[x] ,d ; i ; i = h[i].next){
if((d = h[i].node) == hson[x] || d == fa[x]) continue;
dfs2(d);
}
cur[a[x] >> 6] ^= 1ull << (a[x] & 63);
}
int isanc(int anc ,int x){
return id[anc] <= id[x] && id[x] < id[anc] + sz[anc];
}
void getit(int x){
if(setup[x]){
for(int i = 0 ; i < MX / 64 + 1 ; ++i){
cur[i] ^= f[bid[x]][i];
}
return ;
}
cur[a[x] >> 6] ^= 1ull << (a[x] & 63);
getit(fa[x]);
}
int query(int x ,int y ,int k){
memset(cur ,0 ,sizeof cur);
getit(x) ,getit(y);
int lca = LCA(x ,y);
cur[a[lca] >> 6] ^= 1ull << (a[lca] & 63);
// obitset(cur ,30000);
int all = 0 ,ans = 0;
for(int i = 0 ; i < MX / 64 + 1 ; ++i){
all += __builtin_popcountll(cur[i]);
}
if(all == 0){
ans = -1;
goto nmsl;
}
k = (k - 1) % all + 1;
for(int i = 0 ; i < MX / 64 + 1 ; ++i){
if(k - __builtin_popcountll(cur[i]) <= 0){
for(int j = 0 ; j < 64 ; ++j){
k -= (cur[i] >> j) & 1;
if(!k){
ans = i * 64 + j;
goto nmsl;
}
}
}
k -= __builtin_popcountll(cur[i]);
}
nmsl:
return ans;
}
int main(){
__FILE(set);
n = read() ,m = read();
for(int i = 1 ; i <= n ; ++i){
a[i] = read();
}
for(int i = 1 ,u ,v ; i < n ; ++i){
u = read() ,v = read();
addedge(u ,v);
}
setup[0] = true;
dfs1(1 ,0 ,1);
dfs2(1);
FUCKLCA::main();
for(int i = 1 ,op ,x ,y ,k ; i <= m ; ++i){
op = read();
x = read();
y = read();
if(op == 1){
for(int j = 1 ; j * BSZ <= n ; ++j){
if(isanc(x ,antibid[j])){
f[j][a[x] >> 6] ^= 1ull << (a[x] & 63);
f[j][y >> 6] ^= 1ull << (y & 63);
}
}
a[x] = y;
}
else{
k = read();
printf("%d\n" ,query(x ,y ,k));
}
}
return 0;
}
标签:frac,int,题解,复杂度,互测题,压位,1ull,omega,MX 来源: https://www.cnblogs.com/imakf/p/14505131.html