题目传送门
https://www.luogu.org/problemnew/show/P3613#sub
题解
之前在某tu的博客里看到这题,许久没碰LCT,就翻出来做。
由于上一篇博客的公式敲到我呕心沥血、自废双臂,所以还是没有公式的数据结构题好。
首先考虑一条路径,一个数从那里走一遍,由于二进制里的每一位是独立、互不影响的,于是我们可以每一位分开考虑。
这样只用考虑每一位选0或1从路径经过后的答案即可。明显可以用LCT来做。
当我们读入一个上界时,由于不能超过它,所以每一位不一定取最优,我们从高到低贪心选取,尽量使高位是1且满足要求。低位在高位之后考虑一定更优。
但是这样明显超时。我们分开算每一位导致维护的时间变成k倍。由于互不影响,一起维护也可。于是在LCT操作时搞一堆位运算,然后算答案时直接从每一位拿就行了。
为了方便操作,我们每棵splay维护0和2^k-1经过后的值。由于splay上一棵子树是一段路径,合并子树时先拿左子树和根搞,再从结果中将0提出来和右子树0进入的值&一下,将1提出来和右子树1进入的值&一下,就能得到正确的结果。
这里要注意左右儿子为空的情况,虽然有点烦,但还是很清晰的。
由于路径有方向,所以要维护双向的信息。每次rev操作改变自己信息,但不仅仅改变儿子位置,也改变了儿子的信息,所以每次根up时要将左右儿子的rev先处理了,而一般rev操作不改变值就没必要。
(ps:这里的down自己是为了父亲的up,所以自己的up要先down儿子,而一般的add已经在down自己时维护儿子信息了,注意区别)
这样的时间复杂度就变成了O(nlogn+m*(k+logn))。
注意2^64已经爆了ULL,所以别试着把它写进代码里。
代码
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <cstring>
#define maxn 100010
using namespace std;
typedef unsigned long long ULL;
int n, m, k, cnt;
ULL INF;
struct Tnode{
ULL val, zero, one, rzero, rone;
int type, parent;
bool rev;
Tnode *son[2], *fa;
int Get_d(){return fa->son[1] == this;}
void Connect(Tnode *now, int d){(son[d] = now)->fa = this;}
void Down(){
if(rev){
swap(zero, rzero);
swap(one, rone);
swap(son[0], son[1]);
if(son[0]) son[0]->rev ^= 1;
if(son[1]) son[1]->rev ^= 1;
rev = false;
}
}
void Up(){
ULL A0, A1, B0, B1;
if(son[0]) son[0]->Down();
if(son[1]) son[1]->Down();
if(!son[0]){
if(type == 1) A0 = 0ULL, A1 = val;
else if(type == 2) A0 = val, A1 = INF;
else A0 = val, A1 = INF ^ val;
}
else{
if(type == 1) A0 = son[0]->zero & val, A1 = son[0]->one & val;
else if(type == 2) A0 = son[0]->zero | val, A1 = son[0]->one | val;
else A0 = son[0]->zero ^ val, A1 = son[0]->one ^ val;
}
if(!son[1]) B0 = 0ULL, B1 = INF;
else B0 = son[1]->zero, B1 = son[1]->one;
zero = ((INF ^ A0) & B0) | (A0 & B1);
one = (A1 & B1) | ((INF ^ A1) & B0);
if(!son[1]){
if(type == 1) B0 = 0ULL, B1 = val;
else if(type == 2) B0 = val, B1 = INF;
else B0 = val, B1 = INF ^ val;
}
else{
if(type == 1) B0 = son[1]->rzero & val, B1 = son[1]->rone & val;
else if(type == 2) B0 = son[1]->rzero | val, B1 = son[1]->rone | val;
else B0 = son[1]->rzero ^ val, B1 = son[1]->rone ^ val;
}
if(!son[0]) A0 = 0ULL, A1 = INF;
else A0 = son[0]->rzero, A1 = son[0]->rone;
rzero = ((INF ^ B0) & A0) | (B0 & A1);
rone = (B1 & A1) | ((INF ^ B1) & A0);
}
}tree[maxn], *Node[maxn];
Tnode *NewTnode(int type, ULL val){
tree[cnt].son[0] = tree[cnt].son[1] = tree[cnt].fa = NULL;
tree[cnt].parent = 0;
tree[cnt].val = val;
tree[cnt].type = type;
tree[cnt].rev = false;
return tree+cnt++;
}
void Zig(Tnode *now){
Tnode *last = now->fa;
int d = now->Get_d();
if(now->son[!d]) last->Connect(now->son[!d], d);
else last->son[d] = NULL;
if(last->fa) last->fa->Connect(now, last->Get_d());
else now->fa = NULL;
now->Connect(last, !d);
last->Up();
now->parent = last->parent;
last->parent = 0;
}
void Splay(Tnode *now){
Tnode *last;
while(now->fa){
last = now->fa;
if(last->fa) last->fa->Down();
last->Down(); now->Down();
if(last->fa) (last->Get_d() ^ now->Get_d()) ? Zig(now) : Zig(last);
Zig(now);
}
if(!now->fa) now->Down();
now->Up();
}
void Access(int x){
Splay(Node[x]);
if(Node[x]->son[1]){
Node[x]->son[1]->fa = NULL;
Node[x]->son[1]->parent = x;
Node[x]->son[1] = NULL;
Node[x]->Up();
}
int y = Node[x]->parent;
while(y){
Splay(Node[y]);
if(Node[y]->son[1]){
Node[y]->son[1]->fa = NULL;
Node[y]->son[1]->parent = y;
Node[y]->son[1] = NULL;
Node[y]->Up();
}
Node[y]->Connect(Node[x], 1);
Node[x]->parent = 0;
Node[y]->Up();
x = y;
y = Node[x]->parent;
}
}
void Evert(int x){
Access(x);
Splay(Node[x]);
Node[x]->rev ^= 1;
}
void Link(int x, int y){
Evert(x);
Node[x]->parent = y;
}
void Update(int x, int y, ULL z){
Splay(Node[x]);
Node[x]->type = y;
Node[x]->val = z;
Node[x]->Up();
}
ULL Query(int x, int y, ULL z){
Evert(x);
Access(y);
Splay(Node[y]);
ULL ans0 = Node[y]->zero, ans1 = Node[y]->one, ans = 0ULL, num = 0ULL;
for(int i = k; i >= 0; i--){
if((1ULL << i) & ans0) ans |= (1ULL << i);
else if(((1ULL << i) & ans1) && (num | (1ULL << i)) <= z){
ans |= (1ULL << i);
num |= (1ULL << i);
}
}
return ans;
}
int main(){
scanf("%d%d%d", &n, &m, &k);
for(int i = 0; i < k; i++) INF |= (1ULL << i);
int op, x, y;
ULL z;
for(int i = 1; i <= n; i++){
scanf("%d%llu", &x, &z);
Node[i] = NewTnode(x, z);
Node[i]->Up();
}
for(int i = 1; i < n; i++){
scanf("%d%d", &x, &y);
Link(x, y);
}
for(int i = 1; i <= m; i++){
scanf("%d%d%d%llu", &op, &x, &y, &z);
if(op == 1) printf("%llu\n", Query(x, y, z));
else Update(x, y, z);
}
return 0;
}
抽空填坑。