首页 > 代码库 > 【bzoj4811】[Ynoi2017]由乃的OJ 树链剖分+线段树区间合并

【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 | 1using 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 树链剖分+线段树区间合并