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 树链剖分+线段树区间合并的主要内容,如果未能解决你的问题,请参考以下文章
bzoj4811[Ynoi2017]由乃的OJ 树链剖分+线段树区间合并
BZOJ4811 [Ynoi2017]由乃的OJ 树链剖分 位运算