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

Posted GXZlegend

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4811[Ynoi2017]由乃的OJ 树链剖分+线段树区间合并相关的知识,希望对你有一定的参考价值。

题目描述

由乃正在做她的OJ。现在她在处理OJ上的用户排名问题。OJ上注册了n个用户,编号为1~",一开始他们按照编号
排名。由乃会按照心情对这些用户做以下四种操作,修改用户的排名和编号:然而由乃心情非常不好,因为Deus天
天问她题。。。因为Deus天天问由乃OI题,所以由乃去学习了一下OI,由于由乃智商挺高,所以OI学的特别熟练她
在RBOI2016中以第一名的成绩进入省队,参加了NOI2016获得了金牌保送
Deus:这个题怎么做呀?
yuno:这个不是NOI2014的水题吗。。。
Deus:那如果出到树上,多组链询问,带修改呢?
yuno:诶。。。???
Deus:这题叫做睡觉困难综合征哟~
虽然由乃OI很好,但是她基本上不会DS,线段树都只会口胡,比如她NOI2016的分数就是100+100+100+0+100+100。
。。NOIP2017的分数是100+0+100+100+0+100所以她还是只能找你帮她做了。。。
给你一个有n个点的树,每个点的包括一个位运算opt和一个权值x,位运算有&,l,^三种,分别用1,2,3表示。
每次询问包含三个数x,y,z,初始选定一个数v。然后v依次经过从x到y的所有节点,每经过一个点i,v就变成v opti
 xi,所以他想问你,最后到y时,希望得到的值尽可能大,求最大值?给定的初始值v必须是在[0,z]之间。每次修
改包含三个数x,y,z,意思是把x点的操作修改为y,数值改为z

输入

第一行三个数n,m,k。k的意义是每个点上的数,以及询问中的数值z都 <2^k。之后n行
每行两个数x,y表示该点的位运算编号以及数值
之后n - 1行,每行两个数x,y表示x和y之间有边相连
之后m行,每行四个数,Q,x,y,z表示这次操作为Q(1位询问,2为修改),x,y,z意义如题所述
0 <= n , m <= 100000 , k <= 64

输出

对于每个操作1,输出到最后可以造成的最大刺激度v

样例输入

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

样例输出

7
1
5


题解

树链剖分+线段树区间合并

前置技能:【bzoj3668】[Noi2014]起床困难综合症

对于这道题——那道签到题的加强版,如何想办法拿出树上的一条链时关键。

考虑树剖,并使用线段树区间合并来处理一段区间从左到右&从右到左方向0&1会变成什么。

这样时间复杂度为$O(nk\\log^2n)$,会TLE。

考虑:没有必要对二进制的每一位开一棵线段树,而是维护一棵线段树,记录000...00(2)和111..11(2)会变成什么。

然后区间合并复杂度同样是$O(1)$的,并且可以直接拿出区间每一位的信息。

具体实现上,要注意x->y方向和y->x方向是不同的,因此需要分开处理。

最后按照起床困难综合症的方法贪心即可。

时间复杂度为$O(n(\\log^2n+k))$。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
#define lson l , mid , x << 1
#define rson mid + 1 , r , x << 1 | 1
using namespace std;
typedef unsigned long long ull;
const ull inf = 0xffffffffffffffffll;
struct data
{
	ull l0 , l1 , r0 , r1;
	data() {};
	data(ull L0 , ull L1 , ull R0 , ull R1) {l0 = L0 , l1 = L1 , r0 = R0 , r1 = R1;}
}a[N << 2] , tmp;
int n , head[N] , to[N << 1] , next[N << 1] , cnt , fa[N] , deep[N] , bl[N] , si[N] , pos[N] , tot , opt[N] , kind[N];
ull v[N] , w[N];
void add(int x , int y)
{
	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs1(int x)
{
	int i;
	si[x] = 1;
	for(i = head[x] ; i ; i = next[i])
		if(to[i] != fa[x])
			fa[to[i]] = x , deep[to[i]] = deep[x] + 1 , dfs1(to[i]) , si[x] += si[to[i]];
}
void dfs2(int x , int c)
{
	int i , k = 0;
	bl[x] = c , pos[x] = ++tot , kind[tot] = opt[x] , w[tot] = v[x];
	for(i = head[x] ; i ; i = next[i])
		if(to[i] != fa[x] && si[to[i]] > si[k])
			k = to[i];
	if(k)
	{
		dfs2(k , c);
		for(i = head[x] ; i ; i = next[i])
			if(to[i] != fa[x] && to[i] != k)
				dfs2(to[i] , to[i]);
	}
}
data rev(data a)
{
	return data(a.r0 , a.r1 , a.l0 , a.l1);
}
data merge(data a , data b)
{
	return data((a.l0 & b.l1) | (~a.l0 & b.l0) , (a.l1 & b.l1) | (~a.l1 & b.l0) , (b.r0 & a.r1) | (~b.r0 & a.r0) , (b.r1 & a.r1) | (~b.r1 & a.r0));
}
void build(int l , int r , int x)
{
	if(l == r)
	{
		switch(kind[l])
		{
			case 1: a[x].l0 = a[x].r0 = 0 , a[x].l1 = a[x].r1 = w[l]; break;
			case 2: a[x].l0 = a[x].r0 = w[l] , a[x].l1 = a[x].r1 = inf; break;
			default: a[x].l0 = a[x].r0 = w[l] , a[x].l1 = a[x].r1 = inf ^ w[l];
		}
		return;
	}
	int mid = (l + r) >> 1;
	build(lson) , build(rson);
	a[x] = merge(a[x << 1] , a[x << 1 | 1]);
}
void update(int p , int opt , ull v , int l , int r , int x)
{
	if(l == r)
	{
		switch(opt)
		{
			case 1: a[x].l0 = a[x].r0 = 0 , a[x].l1 = a[x].r1 = v; break;
			case 2: a[x].l0 = a[x].r0 = v , a[x].l1 = a[x].r1 = inf; break;
			default: a[x].l0 = a[x].r0 = v , a[x].l1 = a[x].r1 = inf ^ v; break;
		}
		return;
	}
	int mid = (l + r) >> 1;
	if(p <= mid) update(p , opt , v , lson);
	else update(p , opt , v , rson);
	a[x] = merge(a[x << 1] , a[x << 1 | 1]);
}
data query(int b , int e , int l , int r , int x)
{
	if(b <= l && r <= e) return a[x];
	int mid = (l + r) >> 1;
	if(e <= mid) return query(b , e , lson);
	else if(b > mid) return query(b , e , rson);
	else return merge(query(b , e , lson) , query(b , e , rson));
}
data solve(int x , int y)
{
	data px = data(0 , inf , 0 , inf) , py = px;
	while(bl[x] != bl[y])
	{
		if(deep[bl[x]] > deep[bl[y]]) px = merge(px , rev(query(pos[bl[x]] , pos[x] , 1 , n , 1))) , x = fa[bl[x]];
		else py = merge(query(pos[bl[y]] , pos[y] , 1 , n , 1) , py) , y = fa[bl[y]];
	}
	if(deep[x] < deep[y]) py = merge(query(pos[x] , pos[y] , 1 , n , 1) , py);
	else px = merge(px , rev(query(pos[y] , pos[x] , 1 , n , 1)));
	return merge(px , py);
}
int main()
{
	int m , i , q , x , y;
	ull j , z , ans;
	scanf("%d%d%*d" , &n , &m);
	for(i = 1 ; i <= n ; i ++ ) scanf("%d%llu" , &opt[i] , &v[i]);
	for(i = 1 ; i < n ; i ++ ) scanf("%d%d" , &x , &y) , add(x , y) , add(y , x);
	dfs1(1) , dfs2(1 , 1);
	build(1 , n ,1);
	while(m -- )
	{
		scanf("%d%d%d%llu" , &q , &x , &y , &z);
		if(q == 1)
		{
			tmp = solve(x , y) , ans = 0;
			for(j = 1ull << 63 ; j ; j >>= 1)
			{
				if(z >= j && tmp.l1 & j && !(tmp.l0 & j)) ans += j , z -= j;
				else ans += tmp.l0 & j;
			}
			printf("%llu\\n" , ans);
		}
		else update(pos[x] , y , z , 1 , n , 1);
	}
	return 0;
}

 

 

以上是关于bzoj4811[Ynoi2017]由乃的OJ 树链剖分+线段树区间合并的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 4811: [Ynoi2017]由乃的OJ

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

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

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

BZOJ4810: [Ynoi2017]由乃的玉米田

[BZOJ]4810: [Ynoi2017]由乃的玉米田