BZOJ4811 [Ynoi2017]由乃的OJ

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

题目链接:BZOJ4811

正解:树链剖分+线段树

解题报告:

  因为位与位之间互相独立,考虑直观做法,对于每一位维护两个变量,分别表示这一位输入$0$、$1$之后会变成的值。

  这个复杂度是$O(knlog^2n)$,显然不能通过。

  但是我们不难发现,对于不同的位,其$0$、$1$的变化趋势相同,那么可以用位运算优化, 把每个输入$0$、$1$压成两个二进制数,每次的变化可以变成位运算。

  仔细考虑一下含义,不妨设为$x$、$y$两个变量合并。

  那么$x$的$0$最终会变成的值,应该是$0$变成$1$再从$1$变成$0$,或上$0$保持$0$的情况。想想就能推出来了==

  注意合并顺序。

//It is made by ljh2000
//有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#include <string>
#include <queue>
#include <cmath>
#include <ctime>
#define lc root<<1
#define rc root<<1|1
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define reg(i,x) for(int i=first[x];i;i=nxt[i])
using namespace std;
typedef unsigned long long LL;
const int MAXN = 200011;
const int MAXM = 400011;
int n,m,Type[MAXN],ecnt,first[MAXN],nxt[MAXM],to[MAXM],son[MAXN],size[MAXN],top[MAXN],dfn[MAXN],pre[MAXN],deep[MAXN],ql,qr,father[MAXN];
LL chu[MAXN],k,A,mi[100];
bool ok;
struct node{ LL ans0,ans1; }a[MAXN*3],b[MAXN*3],ans,ansx,ansy,tmp;
inline void link(int x,int y){ nxt[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
inline node merge(node q,node qq){ return (node){(q.ans0&qq.ans1) | (~q.ans0&qq.ans0) , (q.ans1&qq.ans1) | (~q.ans1&qq.ans0)}; }
inline LL getint(){
    LL w=0,q=0; char c=getchar(); while((c<‘0‘||c>‘9‘) && c!=‘-‘) c=getchar();
    if(c==‘-‘) q=1,c=getchar(); while (c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar(); return q?-w:w;
}

inline node get(int tt,LL z){
	if(tt==1) return (node){ 0,(~0)&z };
	else if(tt==2) return (node){ z,(~0)|z };
	else return (node){ z,(~0)^z };
}

inline void dfs(int x,int fa){
	size[x]=1;
	reg(i,x) {
		int v=to[i]; if(v==fa) continue;
		deep[v]=deep[x]+1; father[v]=x; dfs(v,x);
		size[x]+=size[v]; if(size[v]>size[son[x]]) son[x]=v;
	}
}

inline void dfs2(int x,int fa){
	dfn[x]=++ecnt; pre[ecnt]=x; if(son[x]) top[son[x]]=top[x],dfs2(son[x],x);
	reg(i,x) {
		int v=to[i]; if(v==fa || v==son[x]) continue;
		top[v]=v; dfs2(v,x);
	}
}

inline void build(int root,int l,int r){
	if(l==r) {
		int x=pre[l];
		a[root]=b[root]=get(Type[x],chu[x]);
		return ;
	}
	int mid=(l+r)>>1; build(lc,l,mid); build(rc,mid+1,r);
	a[root]=merge(a[lc],a[rc]);
	b[root]=merge(b[rc],b[lc]);
}

inline void modify(int root,int l,int r,int pos,int tt,LL z){
	if(l==r) {
		a[root]=get(tt,z);
		b[root]=get(tt,z);
		return ;
	}
	int mid=(l+r)>>1;
	if(pos<=mid) modify(lc,l,mid,pos,tt,z);
	else modify(rc,mid+1,r,pos,tt,z);
	a[root]=merge(a[lc],a[rc]);
	b[root]=merge(b[rc],b[lc]);
}

inline void query(int root,int l,int r,int type){
	if(ql<=l && r<=qr) {
		if(!ok) {
			ok=true;
			tmp=type?a[root]:b[root];
		}
		else {
			if(type) tmp=merge(tmp,a[root]);
			else tmp=merge(tmp,b[root]);
		}
		return ;
	}
	int mid=(l+r)>>1;
	if(qr<=mid) query(lc,l,mid,type);
	else if(ql>mid) query(rc,mid+1,r,type);
	else {
		if(type) query(lc,l,mid,type),query(rc,mid+1,r,type);//down y!!!
		else query(rc,mid+1,r,type),query(lc,l,mid,type);//up x!!!
	}
}

inline void lca(int x,int y){
	int f1=top[x],f2=top[y];
	bool flag1=false,flag2=false;
	while(f1!=f2) {
		if(deep[f1]<deep[f2]) {
			ql=dfn[f2]; qr=dfn[y]; ok=false;
			query(1,1,n,1);
			y=father[f2]; f2=top[y];
			if(!flag2) ansy=tmp,flag2=true;
			else ansy=merge(tmp,ansy);
		}
		else {
			ql=dfn[f1]; qr=dfn[x]; ok=false;
			query(1,1,n,0);
			x=father[f1]; f1=top[x];
			if(!flag1) ansx=tmp,flag1=true;
			else ansx=merge(ansx,tmp);
		}
	}
	if(deep[x]<deep[y]) {
		ql=dfn[x]; qr=dfn[y]; ok=false;
		query(1,1,n,1);
		if(!flag2) ansy=tmp,flag2=true;//!!!
		else ansy=merge(tmp,ansy);
	}
	else {
		ql=dfn[y]; qr=dfn[x]; ok=false;
		query(1,1,n,0);
		if(!flag1) ansx=tmp,flag1=true;//!!!
		else ansx=merge(ansx,tmp);
	}
	if(!flag1) ans=ansy;
	else if(!flag2) ans=ansx;
	else ans=merge(ansx,ansy);
}

inline void work(){
	n=getint(); m=getint(); k=getint(); for(int i=1;i<=n;i++) Type[i]=getint(),chu[i]=getint();
	int type,x,y; LL z,now; for(int i=1;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); }
	deep[1]=1; dfs(1,0);
	ecnt=0; dfs2(1,0);
	build(1,1,n);
	mi[0]=1; for(int i=1;i<=64;i++) mi[i]=mi[i-1]*2;
	while(m--) {
		type=getint();
		if(type==1) {
			x=getint(); y=getint(); z=getint();
			lca(x,y);
			A=0; now=0;
			for(long long i=k;i>=0;i--) {
				if((ans.ans0>>i)&1) A+=mi[i];
				else if(( ans.ans1>>i &1 ) && (now+mi[i]<=z)){
					A+=mi[i];
					now+=mi[i];
				}
			}
			//cout<<A<<endl;
			printf("%llu\n",A);
		}
		else {
			x=getint(); y=getint(); z=getint();
			modify(1,1,n,dfn[x],y,z);
		}
	}
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("4811.in","r",stdin);
	freopen("4811.out","w",stdout);
#endif
    work();
    return 0;
}
//有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。

  

时间: 2024-10-19 21:29:01

BZOJ4811 [Ynoi2017]由乃的OJ的相关文章

BZOJ4811 [Ynoi2017]由乃的OJ 树链剖分 位运算

原文链接 题目传送门 - BZOJ4811 题意概括 是BZOJ3668长在树上并加上修改和区间询问. 一棵树,n个节点,每一个节点有一个位运算符和一个运算数. 现在要你支持两种操作: 1. 单点修改. 2. 现在你有一个数字v,让他从x走到y,每到达一个节点进行相应的运算.v在0~z之间,让你使得运算结果最大,问v为何值. 题解 我们考虑树链剖分+线段树. 假设某一位为0或者1,那么经过一定的操作之后也是0或1. 那么,如果只有一位,那么两段就可以轻松合并了. k位也是一样,我们只需要用一堆奇

bzoj4811 [Ynoi2017]由乃的OJ 树链剖分+贪心+二进制

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4811 题解 我现在为什么都写一题,调一天啊,马上真的退役不花一分钱了. 考虑这道题的弱化版 NOI2014 起床困难综合证的做法. 分成每一位来考虑,如果高位可以是 \(1\) 的话,那么尽量让高位为 \(1\). 求出当前位为 \(0/1\) 时,最终得到的是 \(0\) 还是 \(1\).因为要保证选的数小于 \(z\),所以对于都可以得到 \(1\) 的情况,尽量选择 \(0\) 可以

【BZOJ4811】[Ynoi2017]由乃的OJ 树链剖分+线段树

[BZOJ4811][Ynoi2017]由乃的OJ Description 由乃正在做她的OJ.现在她在处理OJ上的用户排名问题.OJ上注册了n个用户,编号为1-",一开始他们按照编号排名.由乃会按照心情对这些用户做以下四种操作,修改用户的排名和编号:然而由乃心情非常不好,因为Deus天天问她题...因为Deus天天问由乃OI题,所以由乃去学习了一下OI,由于由乃智商挺高,所以OI学的特别熟练她在RBOI2016中以第一名的成绩进入省队,参加了NOI2016获得了金牌保送 Deus:这个题怎么做

【bzoj4811】[Ynoi2017]由乃的OJ 树链剖分+线段树区间合并

题目描述 由乃正在做她的OJ.现在她在处理OJ上的用户排名问题.OJ上注册了n个用户,编号为1-",一开始他们按照编号 排名.由乃会按照心情对这些用户做以下四种操作,修改用户的排名和编号:然而由乃心情非常不好,因为Deus天 天问她题...因为Deus天天问由乃OI题,所以由乃去学习了一下OI,由于由乃智商挺高,所以OI学的特别熟练她 在RBOI2016中以第一名的成绩进入省队,参加了NOI2016获得了金牌保送 Deus:这个题怎么做呀? yuno:这个不是NOI2014的水题吗... Deu

bzoj 4811: [Ynoi2017]由乃的OJ

树链剖分,用zkw线段树维护每条链两个方向上对每一位的变换情况,由于位数较少,可以用两个unsigned long long表示 #include<cstdio> typedef unsigned long long u64; const int N=100007; char buf[N*100],*ptr=buf-1; int _(){ int x=0,c=*++ptr; while(c<48)c=*++ptr; while(c>47)x=x*10+c-48,c=*++ptr;

LeetCode OJ - Sum Root to Leaf Numbers

这道题也很简单,只要把二叉树按照宽度优先的策略遍历一遍,就可以解决问题,采用递归方法越是简单. 下面是AC代码: 1 /** 2 * Sum Root to Leaf Numbers 3 * 采用递归的方法,宽度遍历 4 */ 5 int result=0; 6 public int sumNumbers(TreeNode root){ 7 8 bFSearch(root,0); 9 return result; 10 } 11 private void bFSearch(TreeNode ro

LeetCode OJ - Longest Consecutive Sequence

这道题中要求时间复杂度为O(n),首先我们可以知道的是,如果先对数组排序再计算其最长连续序列的时间复杂度是O(nlogn),所以不能用排序的方法.我一开始想是不是应该用动态规划来解,发现其并不符合动态规划的特征.最后采用类似于LRU_Cache中出现的数据结构(集快速查询和顺序遍历两大优点于一身)来解决问题.具体来说其数据结构是HashMap<Integer,LNode>,key是数组中的元素,所有连续的元素可以通过LNode的next指针相连起来. 总体思路是,顺序遍历输入的数组元素,对每个

LeetCode OJ - Surrounded Regions

我觉得这道题和传统的用动规或者贪心等算法的题目不同.按照题目的意思,就是将被'X'围绕的'O'区域找出来,然后覆盖成'X'. 那问题就变成两个子问题: 1. 找到'O'区域,可能有多个区域,每个区域'O'都是相连的: 2. 判断'O'区域是否是被'X'包围. 我采用树的宽度遍历的方法,找到每一个'O'区域,并为每个区域设置一个value值,为0或者1,1表示是被'X'包围,0则表示不是.是否被'X'包围就是看'O'区域的边界是否是在2D数组的边界上. 下面是具体的AC代码: class Boar

light oj 1236 【大数分解】

给定一个大数,分解质因数,每个质因子的个数为e1,e2,e3,--em, 则结果为((1+2*e1)*(1+2*e2)--(1+2*em)+1)/2. //light oj 1236 大数分解素因子 #include <stdio.h> #include <iostream> #include <string.h> #include <algorithm> #include <math.h> #include <ctype.h> #i