bzoj4811: [Ynoi2017]由乃的OJ 树链剖分+线段树 拆位

bzoj4811: [Ynoi2017]由乃的OJ

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

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,输出到最后可以造成的最大刺激度v

Sample Input

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

Sample Output

7
1
5

分析

题目原型是 [Noi2014]起床困难综合症
这题把他搬到了树上,加上了修改。
实际上没差,还是一样的做法,只不过线段树中的每个节点代表的是从某一个方向带入 0 0 0 1111 ⋯ 1111\cdots 1111进入区间运算后的结果。
然后上树剖就 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

代码

巨难调,要注意方向。
zkw真好玩。

#include<bits/stdc++.h>
#define pa std::pair<ULL, ULL>
#define f0 first
#define f1 second
#define mp std::make_pair
typedef unsigned long long ULL;
const int N = 1e5 + 10, Nt = 131072; const ULL ALL = -1;
ULL ri() {
	char c = getchar(); ULL x = 0; for(;c < '0' || c > '9'; c = getchar()) ;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x;
}
int sz[N], ds[N], in[N], d[N], de[N], pr[N], fa[N], nx[N << 1], to[N << 1];
int st1[N], st2[N], ps[N], opt[N], tp, tot, K; pa st[N]; ULL val[N], bin[64];
void add(int u, int v) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp;}
void adds(int u, int v) {add(u, v); add(v, u);}
ULL Cal(int o, ULL v, ULL st) {return o == 1 ? v & st : (o == 2 ? v | st : v ^ st);}
struct D {pa f[2];void Ini(int o, ULL v) {f[0] = f[1] = mp(Cal(o, v, 0), Cal(o, v, ALL));}}T[Nt << 1];
pa operator+(pa a, pa b) {
	pa r;
	r.f0 = (a.f0&b.f1)|(~a.f0&b.f0);
	r.f1 = (a.f1&b.f1)|(~a.f1&b.f0);
	return r;
}
D operator+(D a, D b) {
	D r;
	r.f[0] = a.f[0] + b.f[0];
	r.f[1] = b.f[1] + a.f[1];
	return r;
}
void Up(int i, int o, ULL v) {for(T[i += Nt].Ini(o, v); i >>= 1; T[i] = T[i << 1] + T[i << 1 | 1]);}
pa Que(int s, int t, bool p) {
	int tp1 = 0, tp2 = 0;
	for(s += Nt - 1, t += Nt + 1;s ^ t ^ 1; s >>= 1, t >>= 1) {
		if(~s & 1) st1[++tp1] = s ^ 1;
		if(t & 1) st2[++tp2] = t ^ 1;
	}
	for(;tp2;) st1[++tp1] = st2[tp2--];
	pa r; int i = 2;
	if(p) for(r = T[st1[tp1]].f[p];--tp1;) r = r + T[st1[tp1]].f[p];
	else for(r = T[st1[1]].f[p];i <= tp1; ++i) r = r + T[st1[i]].f[p]; 
	return r;
}
void dfs1(int u, int ff) {
	sz[u] = 1; fa[u] = ff; de[u] = de[ff] + 1;
	for(int i = pr[u]; i; i = nx[i])
		if(to[i] != ff) {
			dfs1(to[i], u), sz[u] += sz[to[i]];
			sz[ds[u]] < sz[to[i]] ? ds[u] = to[i] : 0;
		}
}
void dfs2(int u, int c) {
	d[u] = c; in[u] = ++tot; ps[tot] = u; 
	if(!ds[u]) return ; dfs2(ds[u], c);
	for(int i = pr[u]; i; i = nx[i])
		if(to[i] != fa[u] && to[i] != ds[u]) 
			dfs2(to[i], to[i]);
}
int Lca(int u, int v) {
	for(;d[u] != d[v]; u = fa[d[u]]) if(de[d[u]] < de[d[v]]) u ^= v ^= u ^= v;
	return de[u] < de[v] ? u : v;
}
pa Link(int u, int c, bool p) {
	int x = de[c] + p, tp = 0; 
	for(;de[d[u]] >= x; u = fa[d[u]]) st[++tp] = Que(in[d[u]], in[u], p);
	if(de[u] >= x) st[++tp] = Que(in[u] - de[u] + x, in[u], p);
	pa r; int i = 2;
	if(!p) for(r = st[tp];--tp;) r = r + st[tp];
	else for(r = st[1];i <= tp; ++i) r = r + st[i];
	return r;
}
ULL Solve(int u, int v, ULL M) {
	int c = Lca(u, v); ULL ans = 0; pa r;
	r = Link(v, c, 0); if(u != c) r = Link(u, c, 1) + r;
	for(int i = K - 1; ~i; --i) {
		bool c0 = r.f0 & bin[i], c1 = r.f1 & bin[i];
		if(c0 >= c1 || bin[i] > M) ans |= c0 ? bin[i] : 0;
		else ans |= c1 ? bin[i] : 0, M -= bin[i];
	}
	return ans;
}
int main() {
	int n = ri(), m = ri(); K = ri();
	bin[0] = 1; for(int i = 1;i < K; ++i) bin[i] = bin[i - 1] << 1;
	for(int i = 1;i <= n; ++i) opt[i] = ri(), val[i] = ri();
	for(int i = 1;i < n; ++i) adds(ri(), ri());
	dfs1(1, 0); dfs2(1, 1); 
	for(int i = 1;i <= n; ++i) T[i + Nt].Ini(opt[ps[i]], val[ps[i]]);
	for(int i = Nt - 1;i; --i) T[i] = T[i << 1] + T[i << 1 | 1];
	for(;m--;) {
		int op = ri(), x = ri(), y = ri(); ULL z = ri();
		if(op == 1) printf("%llu\n", Solve(x, y, z));
		else Up(in[x], y, z);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值