P5354 [Ynoi Easy Round 2017] 由乃的 OJ

题意:

题目背景

题目描述

由乃正在做她的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(nklog⁡2n)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]);
		}
	}
}
相关推荐
代码无bug抓狂人1 小时前
C语言之单词方阵——深搜(很好的深搜例题)
c语言·开发语言·算法·深度优先
青桔柠薯片1 小时前
Linux软件编程:线程和进程间通信
linux·开发语言·线程·进程
让我上个超影吧1 小时前
消息队列——RabbitMQ(高级)
java·rabbitmq
foundbug9991 小时前
基于C# WinForm实现串口数据读取与实时折线图显示
开发语言·c#
im_AMBER1 小时前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
Polaris北1 小时前
第二十九天打卡
算法
样例过了就是过了2 小时前
LeetCode热题100 环形链表 II
数据结构·算法·leetcode·链表
码农幻想梦2 小时前
3472. 八皇后(北京大学考研机试题目)
考研·算法·深度优先
得物技术2 小时前
Sentinel Java客户端限流原理解析|得物技术
java·后端·架构