BZOJ-4811: [Ynoi2017]由乃的OJ (树链剖分 线段树维护区间操作值 好题)
2017-10-16 12:46
531 查看
4811: [Ynoi2017]由乃的OJ
Time Limit: 6 Sec Memory Limit: 256 MBSubmit: 366 Solved: 118
[Submit][Status][Discuss]
Description
由乃正在做她的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
![](http://www.lydsy.com/JudgeOnline/upload/201704/vv2.png)
Input
第一行三个数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
Output
对于每个操作1,输出到最后可以造成的最大刺激度vSample Input
5 5 31 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
Sample Output
71
5
#include <bits/stdc++.h> using namespace std; #define maxn 100005 vector<vector<int> >g(maxn); struct point{ int op; unsigned long long v; }a[maxn]; struct tree{ unsigned long long v0, v1; tree() {}; friend tree operator + (tree x, tree y){ //重载+方便后面合并操作 tree z; z.v0 = (x.v0 & y.v1) | ((~x.v0) & y.v0); z.v1 = (x.v1 & y.v1) | ((~x.v1) & y.v0); return z; } tree (int op, unsigned long long v){ //构造函数 if(op == 1){ v0 = 0 & v; v1 = (~0) & v; } if(op == 2){ v0 = 0 | v; v1 = (~0) | v; } if(op == 3){ v0 = 0 ^ v; v1 = (~0) ^ v; } } }cl[maxn << 2], cr[maxn << 2]; //因为从结点x走到y,链剖后的分段路径会有从上到下和从下到上的区别,这里需要区分 int sz[maxn], id[maxn], dep[maxn], son[maxn], top[maxn], pre[maxn], val[maxn], tot; void dfs(int x, int fa, int d){ sz[x] = 1; pre[x] = fa; dep[x] = d; son[x] = 0; int cur; for(int i = 0; i < g[x].size(); ++i){ cur = g[x][i]; if(cur == fa) continue; dfs(cur, x, d + 1); sz[x] += sz[cur]; if(sz[son[x]] < sz[cur]){ son[x] = cur; } } } void dfs1(int x, int tp){ id[x] = ++tot; top[x] = tp; if(son[x]) dfs1(son[x], tp); int cur; for(int i = 0; i < g[x].size(); ++i){ cur = g[x][i]; if(cur == pre[x] || cur == son[x]) continue; dfs1(cur, cur); } } void build(int o, int l, int r){ if(l == r){ cl[o] = cr[o] = tree(a[val[l]].op, a[val[l]].v); return; } int mid = l + r >> 1; build(o << 1, l, mid); build(o << 1 | 1, mid + 1, r); cl[o] = cl[o << 1] + cl[o << 1 | 1]; cr[o] = cr[o << 1 | 1] + cr[o << 1]; //合并上的区别,值得注意 } void add(int o, int l, int r, int pos, int op, unsigned long long z){ if(l == r){ cl[o] = cr[o] = tree(op, z); return; } int mid = l + r >> 1; if(pos <= mid) add(o << 1, l, mid, pos, op, z); else add(o << 1 | 1, mid + 1, r, pos, op, z); cl[o] = cl[o << 1] + cl[o << 1 | 1]; //注意合并的顺序 cr[o] = cr[o << 1 | 1] + cr[o << 1]; } tree query(int o, int l, int r, int L, int R, int kind){ if(l >= L && r <= R){ if(kind == 1) return cl[o]; return cr[o]; } int mid = l + r >> 1; if(mid >= R) return query(o << 1, l, mid, L, R, kind); else if(mid < L) return query(o << 1 | 1, mid + 1, r, L, R, kind); else{ if(kind == 1) return query(o << 1, l, mid, L, R, kind) + query(o << 1 | 1, mid + 1, r, L, R, kind); else return query(o << 1 | 1, mid + 1, r, L, R, kind) + query(o << 1, l, mid, L, R, kind); //注意合并的顺序 } } unsigned long long getsm(int x, int y, unsigned long long z, int k){ int tp1 = top[x], tp2 = top[y]; tree ansx = tree(3, 0), ansy = tree(3, 0); while(tp1 != tp2){ if(dep[tp1] < dep[tp2]){ ansy = query(1, 1, tot, id[tp2], id[y], 1) + ansy; //还是注意顺序。。。。 y = pre[tp2]; tp2 = top[y]; } else{ ansx = ansx + query(1, 1, tot, id[tp1], id[x], 2); x = pre[tp1]; tp1 = top[x]; } } if(dep[x] < dep[y]){ ansx = ansx + query(1, 1, tot, id[x], id[y], 1) + ansy; //注意合并的顺序 } else{ ansx = ansx + query(1, 1, tot, id[y], id[x], 2) + ansy; } unsigned long long res = 0, cost = 0; for(int i = k - 1; i >= 0; --i){ //这里一定全程unsigned long long!!!! if((ansx.v0 >> i) & 1ULL * 1){ res |= 1ULL * 1 << i; } else if((ansx.v1 >> i) & 1ULL * 1){ if((cost | (1ULL * 1 << i)) <= z){ res |= 1ULL * 1 << i; cost |= 1ULL * 1 << i; } } } return res; } int main(){ int n, m, k, u, v, q; unsigned long long z; scanf("%d %d %d", &n, &m, &k); for(int i = 1; i <= n; ++i){ scanf("%d %llu", &a[i].op, &a[i].v); } for(int i = 1; i < n; ++i){ scanf("%d %d", &u, &v); g[u].push_back(v); g[v].push_back(u); } tot = 0; dfs(1, 0, 1); dfs1(1, 1); for(int i = 1; i <= n; ++i){ val[id[i]] = i; } build(1, 1, tot); for(int i = 1; i <= m; ++i){ scanf("%d %d %d %llu", &q, &u, &v, &z); if(q == 2){ add(1, 1, tot, id[u], v, z); } else{ printf("%llu\n", getsm(u, v, z, k)); } } } /* 题意:一棵树,1e5个结点,每个结点一个操作(&,|,^)和一个数值。1e5次操作 ,每次操作要么修改每个结点的操作和 数值,要么询问路径x到y上,在[0,z]中找个数对和每个结点上的数字进行一次该结点上的操作,使得最终答案最大。 思路:一开始想可以直接把操作和数字合并啊,然后直接树链剖分然后线段树维护区间值,然后发现naive了,&,|,^这三个 位运算没办法用一个数搞定,那么就用两个数好了。。。。v0,v1分别状压(这三种位运算是位独立的,位和位互不影响)表示每一位 原本是0经过操作后得到什么,和原本是1经过操作后得到什么。所以操作&和数字可以合并为v0 = 0 & v; v1 = (~0) & v, 其余的类推,然后我们 就不用管操作符了。之后就可以用线段树维护区间的值了,这里我们得讨论合并操作,就是v0在经过x,再经过y后怎么表示。 首先v0在经过x后,有些位变成1了,这个时候我们用v1.y更新这些位,很容易理解吧,即 (x.v0 & y.v1);还有一些位没有变成0,还 保持着0怎么办,当然用v0.y更新这些位了- -||.即((~x.v0) & y.v0),x.v0取非的原因是还保持0的那些位直接&的话一定是0,无法体现 y.v0的作用,实际上这里如果单独考虑还是0的位直接用y.v0就好了,但是避免不是0的位的影响,所以用~x.v0,这样非0的变成0,不考虑这些位, 为0的变成1,和y.v0 &,保留y.v0对这些位的贡献。然后 | 一下就好了,合并两者各自位的贡献。 注意:从x到y后操作的先后顺序,这样我们需要区分合并区间的顺序,这个很简单,用两个数组存就好了,合并的时候注意一下。然后由于最大64 位,全程都用unsigned long long!!!WA了多次才意识到。。。。 */
相关文章推荐
- [YNOI2017][bzoj4811][luogu3613] 由乃的OJ/睡觉困难综合症 [压位+树链剖分+线段树]
- [树链剖分] BZOJ 4811 [Ynoi2017]由乃的OJ
- bzoj 4811: [Ynoi2017]由乃的OJ (树链剖分)
- BZOJ 4811([Ynoi2017]由乃的OJ-树链剖分)
- 【bzoj4811】[Ynoi2017]由乃的OJ 树链剖分+线段树区间合并
- bzoj 4811: [Ynoi2017]由乃的OJ
- BZOJ-2243: [SDOI2011]染色 (树链剖分 入门题 线段树 区间修改查询 维护端点值)
- BZOJ4811 [Ynoi2017]由乃的OJ
- BZOJ 4811 [Ynoi2017]由乃的OJ ——Link-Cut Tree
- bzoj 4811: [Ynoi2017]由乃的OJ
- 【BZOJ4811】[Ynoi2017]由乃的OJ 树链剖分+线段树
- [BZOJ4811][Ynoi2017]由乃的OJ 树链剖分+线段树
- [bzoj4811] [Ynoi2017]由乃的OJ
- 【BZOJ4515】游戏,树链剖分+永久化标记线段树维护线段信息(李超线段树)
- 【BZOJ3878】【Ahoi2014】奇怪的计算器 维护区间性质。线段树
- gym101138J(树链剖分,线段树维护区间连续子段最大和,好题)
- BZOJ 5039([Jsoi2014]序列维护-线段树区间加,区间乘,区间求和)
- BZOJ 2243 [SDOI2011]染色 树链剖分+LCA+区间合并线段树
- 【bzoj1593-预定旅馆】线段树维护连续区间
- 【bzoj2325】[ZJOI2011]道馆之战 树链剖分+线段树区间合并