题意:
题目背景

题目描述
由乃正在做她的OJ。现在她在处理OJ上的用户排名问题。
OJ 上注册了 nnn 个用户,编号为 1∼n1\sim n1∼n,一开始他们按照编号排名。由乃会按照心情对这些用户做以下四种操作,修改用户的排名和编号:然而由乃心情非常不好,因为 Deus 天天问她题。。。
因为 Deus 天天问由乃 OI 题,所以由乃去学习了一下 OI,由于由乃智商挺高,所以 OI 学的特别熟练。
她在 RBOI2016 中以第一名的成绩进入省队,参加了 NOI2016 获得了金牌保送。
Deus:这个题怎么做呀?
yuno:这个不是 NOI2014 的水题吗。。。
Deus:那如果出到树上,多组链询问,带修改呢?
yuno:诶。。。???
Deus:这题叫做睡觉困难综合征哟~
虽然由乃 OI 很好,但是她基本上不会 DS,线段树都只会口胡,比如她 NOI2016 的分数就是 100+100+100+0+100+100100+100+100+0+100+100100+100+100+0+100+100。。。NOIP2017 的分数是 100+0+100+100+0+100100+0+100+100+0+100100+0+100+100+0+100。
所以她还是只能找你帮她做了。。。
给你一个有 nnn 个点的树,每个点的包括一个位运算 optoptopt 和一个权值 xxx,位运算有&,|,^ 三种,分别用 1,2,31,2,31,2,3 表示。
每次询问包含三个整数 x,y,zx,y,zx,y,z,初始选定一个数 vvv。然后 vvv 依次经过从 xxx 到 yyy 的所有节点,每经过一个点 iii,vvv 就变成 v opti xiv\ opt_i\ x_iv opti xi,所以他想问你,最后到 yyy 时,希望得到的值尽可能大,求最大值。给定的初始值 vvv 必须是在 [0,z][0,z][0,z] 之间。
每次修改包含三个整数 x,y,zx,y,zx,y,z,意思是把 xxx 点的操作修改为 yyy,数值改为 zzz。
输入格式
第一行三个整数 n,m,kn,m,kn,m,k。kkk 的意义是每个点上的数,以及询问中的数值 zzz 都小于 2k2^k2k。
之后 nnn 行,每行两个整数 x,yx,yx,y 表示该点的位运算编号以及数值。
之后 n−1n - 1n−1 行,每行两个数 x,yx,yx,y 表示 xxx 和 yyy 之间有边相连。
之后 mmm 行,每行四个数,Q,x,y,zQ,x,y,zQ,x,y,z 表示这次操作为 QQQ(111 为询问,222 为修改),x,y,zx,y,zx,y,z 意义如题所述。
输出格式
对于每个操作 111,输出到最后可以得到的最大值。
输入输出样例 #1
输入 #1
5 5 3
1 7
2 6
3 7
3 6
3 1
1 2
2 3
3 4
1 5
1 1 4 7
1 1 3 5
2 1 1 3
2 3 3 3
1 1 3 2
输出 #1
7
1
5
输入输出样例 #2
输入 #2
2 2 2
2 2
2 2
1 2
2 2 2 2
1 2 2 2
输出 #2
3
说明/提示
Idea:f321dd,Solution:f321dd&nzhtl1477,Code:nzhtl1477,Data:nzhtl1477
对于 30%30\%30% 的数据,n,m≤1n,m\leq 1n,m≤1。
对于另外 20%20\%20% 的数据,k≤5k\leq 5k≤5。
对于另外 20%20\%20% 的数据,位运算只会出现一种。
对于 100%100\%100% 的数据,0≤n,m≤1050\leq n,m \leq 10^50≤n,m≤105,0≤k≤640\leq k\leq 640≤k≤64。
题解
首先我们如果在一个区间上,我们应该怎么做。显然,位运算每一个元素互不影响,那么我们可以一位一位地去处理,高位优先变一。那么将这个做法搬到树上,树剖加线段树维护每一个位,复杂度是O(nklog2n)O(nk\log^2 n)O(nklog2n)太高,无法通过。
如何优化呢?
我们可以优化掉k(其他都是树剖线段树固有优化不掉的),我们可以考虑再把这些二进制位压回同一个数字里面
还是上文的结论位运算每一个元素互不影响
情况一
当 f0[l][i]=1f_{0}[l][i] = 1f0[l][i]=1 且 f1[r][i]=1f_{1}[r][i] = 1f1[r][i]=1 时: f0[u][i]=(f0[l][i]∧f1[r][i])∨(¬f0[l][i]∧f0[r][i]) f_{0}[u][i] = (f_{0}[l][i] \land f_{1}[r][i]) \lor (\lnot f_{0}[l][i] \land f_{0}[r][i]) f0[u][i]=(f0[l][i]∧f1[r][i])∨(¬f0[l][i]∧f0[r][i])
扩展到整个 f0[u]f_{0}[u]f0[u] 的表达式: f0[u]=(f0[l]∧f1[r])∨(¬f0[l]∧f1[r]) f_{0}[u] = (f_{0}[l] \land f_{1}[r]) \lor (\lnot f_{0}[l] \land f_{1}[r]) f0[u]=(f0[l]∧f1[r])∨(¬f0[l]∧f1[r])
情况二
类似地,f1[u]f_{1}[u]f1[u] 的表达式为: f1[u]=(f1[l]∧f1[r])∨(¬f1[l]∧f0[r]) f_{1}[u] = (f_{1}[l] \land f_{1}[r]) \lor (\lnot f_{1}[l] \land f_{0}[r]) f1[u]=(f1[l]∧f1[r])∨(¬f1[l]∧f0[r])
线段树维护这些,查询的时候用序列上的贪心,这题就完了
代码:Ynoi不卡常,吓哭了
cpp
#include<bits/stdc++.h>
#define lim ~0ull
using namespace std;
int n,m,r,cnt;
vector<int> e[200010];
int op[200010],son[200010],a[200010],top[200010],fa[200010],id[200010];
int dep[200010],siz[200010],back[200010];
unsigned long long w[200010];
int res;
struct node{
unsigned long long f0,f1,inv0,inv1;
node(){f0=f1=inv0=inv1=0;}
}tree[800001];
unsigned long long calc(unsigned long long num,int u){
if(op[u]==1){
return num&w[u];
}
if(op[u]==2){
return num|w[u];
}
if(op[u]==3){
return num^w[u];
}
}
node up(node ls,node rs){
node x;
x.f0=(ls.f0&rs.f1)|((~ls.f0)&rs.f0);
x.f1=(ls.f1&rs.f1)|((~ls.f1)&rs.f0);
x.inv0=(rs.inv0&ls.inv1)|((~rs.inv0)&ls.inv0);
x.inv1=(rs.inv1&ls.inv1)|((~rs.inv1)&ls.inv0);
return x;
}
void dfs1(int u,int f,int deep){
dep[u]=deep;
fa[u]=f;
siz[u]=1;
for(int i:e[u]){
if(i==f){
continue;
}
dfs1(i,u,deep+1);
siz[u]+=siz[i];
if(siz[i]>siz[son[u]]){
son[u]=i;
}
}
}
void dfs2(int u,int topf){
id[u]=++cnt;
top[u]=topf;
back[cnt]=u;
if(!son[u]){
return ;
}
dfs2(son[u],topf);
for(int i:e[u]){
if(i==fa[u]||i==son[u]){
continue;
}
dfs2(i,i);
}
}
void build(int u,int l,int r){
if(l==r){
tree[u].f0=tree[u].inv0=calc(0ull,back[l]);
tree[u].f1=tree[u].inv1=calc(lim,back[l]);
return;
}
int mid=(l+r)>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
tree[u]=up(tree[u<<1],tree[u<<1|1]);
}
void gai(int u,int l,int r,int pos){
if(l==r){
tree[u].f0=tree[u].inv0=calc(0ull,back[l]);
tree[u].f1=tree[u].inv1=calc(lim,back[l]);
return;
}
int mid=(l+r)>>1;
if(pos<=mid) gai(u<<1,l,mid,pos);
else gai(u<<1|1,mid+1,r,pos);
tree[u]=up(tree[u<<1],tree[u<<1|1]);
}
node find(int u,int l,int r,int ql,int qr){
if(l==ql&&r==qr) return tree[u];
int mid=(l+r)>>1;
node re;
if(qr<=mid) re=find(u<<1,l,mid,ql,qr);
else{
if(ql>mid) re=find(u<<1|1,mid+1,r,ql,qr);
else{
re=up(find(u<<1,l,mid,ql,mid),find(u<<1|1,mid+1,r,mid+1,qr));
}
}
return re;
}
node ans1[200010],ans2[200010];
node treesum(int x,int y){
int cnt1=0,cnt2=0;
while(top[x]!=top[y]){
if(dep[top[x]]>=dep[top[y]]){
ans1[++cnt1]=find(1,1,n,id[top[x]],id[x]);
x=fa[top[x]];
}
else{
ans2[++cnt2]=find(1,1,n,id[top[y]],id[y]);
y=fa[top[y]];
}
}
if(dep[x]>dep[y]){
ans1[++cnt1]=find(1,1,n,id[y],id[x]);
}
else{
ans2[++cnt2]=find(1,1,n,id[x],id[y]);
}
node tmp;
for(int i=1;i<=cnt1;i++){
swap(ans1[i].f0,ans1[i].inv0);
swap(ans1[i].f1,ans1[i].inv1);
}
if(cnt1){
tmp=ans1[1];
for(int i=2;i<=cnt1;i++){
tmp=up(tmp,ans1[i]);
}
if(cnt2){
tmp=up(tmp,ans2[cnt2]);
}
}
else{
tmp=ans2[cnt2];
}
for(int i=cnt2-1;i>=1;i--){
tmp=up(tmp,ans2[i]);
}
return tmp;
}
unsigned long long cha(int t1,int t2,unsigned long long t3){
node tmp=treesum(t1,t2);
unsigned long long ans=0;
for(signed i=63;i>=0;i--){
int tm0=(tmp.f0>>i)&1;
int tm1=(tmp.f1>>i)&1;
if(tm0>=tm1||(1ull<<i)>t3){
ans|=(tm0?(1ull<<i):0);
}
else{
ans|=(tm1?(1ull<<i):0);
t3-=(1ull<<i);
}
}
return ans;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m>>r;
for(int i=1;i<=n;i++){
cin>>op[i]>>w[i];
}
for(int i=1;i<n;i++){
int x,y;
cin>>x>>y;
e[x].push_back(y);
e[y].push_back(x);
}
dfs1(1,0,1);
dfs2(1,1);
build(1,1,n);
while(m--){
int opt,t1,t2;
unsigned long long t3;
cin>>opt>>t1>>t2>>t3;
if(opt==1){
cout<<cha(t1,t2,t3)<<"\n";
}
else{
op[t1]=t2,w[t1]=t3;
gai(1,1,n,id[t1]);
}
}
}