首页 > 其他分享 >【树链剖分】P3401 洛谷树 题解

【树链剖分】P3401 洛谷树 题解

时间:2023-11-21 17:04:17浏览次数:43  
标签:洛谷树 P3401 int 题解 路径 hv 异或 权值 define

P3401

考虑先将路径权值进行转化,因为很难对路径直接进行统计。考虑如何表示出这条路径的权值。记 \(s_i = \oplus_{j \in \text{path}(1, i)} w_j\),其中 \(\text{path}(i, j)\) 表示 \(i\) 到 \(j\) 的路径上的边集。则 \(u\to v\) 的路径的权值为 \(s_u\oplus s_v\)。

现在转变为了求两点路径上任选两点的权值和,以及区间异或。区间异或是因为单次修改一条边的边权会导致其子树内所有点的 \(s\) 值改变,即在 \(u\) 子树内的点都会异或上 \(x\oplus s_u\),\(x\) 指要修改为的值。

不好直接维护,但是看到异或,可以考虑拆位,这样统计和修改都会很简单。统计就是对每一位找到路径中有多少个点的 \(s\) 值的第 \(i\) 位为 \(1\),记为 \(c\),若路径上共有 \(p\) 个点,则答案为 \(2^i\times c\times(p - c)\)。然后修改操作变为区间取反,直接上线段树即可。

时间复杂度:\(\mathcal{O}(n\log^2n\log V)\)。
空间复杂度:\(\mathcal{O}(n\log V)\)。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define vi vector<int>
#define eb emplace_back
#define pii pair<int, ll>
#define fi first
#define se second
#define TIME 1e3 * clock() / CLOCKS_PER_SEC
bool Mbe;
mt19937_64 rng(35);
constexpr int N = 3e5 + 10;
int n, m, dfc;
int s[N], vn[N];
int sz[N], dep[N], hv[N], fa[N], top[N], dfn[N], rdfn[N];
int head[N], cnt_e;
struct edge {
	int to, w, nxt;
} e[N << 1];
void adde(int u, int v, int w) {
	++cnt_e, e[cnt_e].to = v, e[cnt_e].w = w, e[cnt_e].nxt = head[u], head[u] = cnt_e;
}
void dfs1(int u, int ff) {
	fa[u] = ff, dep[u] = dep[ff] + 1, sz[u] = 1;
	for(int i = head[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if(v == ff) continue;
		s[v] = e[i].w ^ s[u];
		vn[v] = e[i].w;
		dfs1(v, u);
		sz[u] += sz[v];
		if(sz[v] > sz[hv[u]]) hv[u] = v;
	}
}
void dfs2(int u, int f) {
	top[u] = f, rdfn[dfn[u] = ++dfc] = u;
	if(!hv[u]) return;
	dfs2(hv[u], f);
	for(int i = head[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if(v == hv[u] || v == fa[u]) continue;
		dfs2(v, v);
	}
}
vi cnt(10), tmp(10);
int val[N << 2][10], laz[N << 2][10];
void tag(int x, int y, int L, int R) {
	val[x][y] = (R - L + 1) - val[x][y];
	laz[x][y] ^= 1;
}
void down(int x, int y, int L, int R) {
	if(laz[x][y]) {
		int m = (L + R) >> 1;
		tag(x << 1, y, L, m);
		tag(x << 1 | 1, y, m + 1, R);
		laz[x][y] = 0;
	}
}
void build(int x, int L, int R) {
	if(L == R) {
		for(int i = 0; i < 10; ++i) val[x][i] = s[rdfn[L]] >> i & 1;
		return;
	}
	int m = (L + R) >> 1;
	build(x << 1, L, m);
	build(x << 1 | 1, m + 1, R);
	for(int i = 0; i < 10; ++i) val[x][i] = val[x << 1][i] + val[x << 1 | 1][i];
}
void modify(int x, int L, int R, int l, int r, int v) {
	if(l <= L && R <= r) {
		for(int i = 0; i < 10; ++i)
			if(v >> i & 1)
				tag(x, i, L, R);
		return;
	}
	for(int i = 0; i < 10; ++i) down(x, i, L, R);
	int m = (L + R) >> 1;
	if(l <= m) modify(x << 1, L, m, l, r, v);
	if(r > m) modify(x << 1 | 1, m + 1, R, l, r, v);
	for(int i = 0; i < 10; ++i)
		val[x][i] = val[x << 1][i] + val[x << 1 | 1][i];
}
void query(int x, int L, int R, int l, int r) {
	if(l <= L && R <= r) {
		for(int i = 0; i < 10; ++i) cnt[i] += val[x][i];
		return;
	}
	for(int i = 0; i < 10; ++i) down(x, i, L, R);
	int m = (L + R) >> 1;
	if(l <= m) query(x << 1, L, m, l, r);
	if(r > m) query(x << 1 | 1, m + 1, R, l, r);
}
ll qry(int u, int v) {
	int au = u, av = v;
	for(int i = 0; i < 10; ++i) cnt[i] = 0;
	while(top[u] ^ top[v]) {
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		query(1, 1, n, dfn[top[u]], dfn[u]);
		u = fa[top[u]];
	}
	if(dfn[u] < dfn[v]) swap(u, v);
	query(1, 1, n, dfn[v], dfn[u]);
	ll ans = 0;
	for(int i = 0; i < 10; ++i) {
		int c1 = cnt[i], c2 = dep[au] + dep[av] - 2 * dep[v] - cnt[i] + 1;
		ans += (1ll << i) * c1 * c2;
	}
	return ans;
}
bool Med;
int main() {
	fprintf(stderr, "%.3lf MB\n", (&Mbe - &Med) / 1048576.0);
//	freopen("P3401_1.in", "r", stdin);
//	freopen("data.out", "w", stdout);
	ios :: sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin >> n >> m;
	for(int i = 1; i < n; ++i) {
		int u, v, w;
		cin >> u >> v >> w;
		adde(u, v, w);
		adde(v, u, w);
	}
	dfs1(1, 0);
	dfs2(1, 1);
	build(1, 1, n);
	for(int i = 1; i <= m; ++i) {
		int opt, x, y, z;
		cin >> opt >> x >> y;
		if(opt == 1) {
			cout << qry(x, y) << "\n";	
		} else {
			cin >> z;
			if(dep[x] < dep[y]) swap(x, y);
			modify(1, 1, n, dfn[x], dfn[x] + sz[x] - 1, vn[x] ^ z);
			vn[x] = z;
		}
	}
	cerr << TIME << "ms\n";
	return 0;
}

标签:洛谷树,P3401,int,题解,路径,hv,异或,权值,define
From: https://www.cnblogs.com/Pengzt/p/17846958.html

相关文章

  • P9620 歌姬 题解
    感觉题解做法都好神秘。来一个容易理解,通俗易懂的树剖解法。思路容易发现原问题等价于维护一个虚树。每一次询问虚树的根的所有儿子的最大值。要求链修。容易发现仅仅动态维护根是好做的。我们用一个\(\text{set}\)。每次维护\(\text{dfs}\)的最小值和最大值。对于这......
  • S7-1200和KTP900basic 调试问题解决
    1:KTP900basic和S7-1200无法通讯?环境:型号:KTP900basic,订货号6AV2123-2JB03-OAX0 博图:V17原因,需要将KTP900basic更新最新的17.0面板镜像,一般需要在软件上额外安装SIMATIC_WinCC_Panel_Images_V17.ISO这个文件,下载连接:精智(Comfort)屏下载时提示缺少面板映像(siemens.com.cn......
  • error:0308010C:digital envelope routines::unsupported问题解决
    问题描述:报错:Error:error:0308010C:digitalenveloperoutines::unsupported报错原因:因为node.jsV17版本中最近发布的OpenSSL3.0,而OpenSSL3.0对允许算法和密钥大小增加了严格的限制报错详细信息:解决方案:方案1:打开IDEA终端,直接输入Linux&MacOS:exportNODE_OPTI......
  • CF1898 D Absolute Beauty 题解
    LinkCF1898DAbsoluteBeautyQuestion给出两个长度都为\(n\)的数组\(a,b\),我们可以任意选择两个数\(i,j\)交换\(b_i\)和\(b_j\)一次,或者不换求\(\sum\limits_{i=1}^n|a_i-b_i|\)的最大值Solution把一个\(a_i,b_i\)抽象成一条线段而交换\(b\)的操作可以......
  • 【题解】JLOI2016 - 成绩比较
    【题解】JLOI2016-成绩比较https://loj.ac/p/2026是我会的题,所以感觉难度不如noipT3T4。设\(f_{i,j}\)表示考虑到前\(i\)门课,有\(j\)人被B碾压。转移,设这轮中有\(k\)个原本被碾压的人不再被碾压,则相当于从\(f_{i-1,j+k}\)转移到\(f_{i,j}\)。考虑转移系数,首......
  • [题解]CF1899D Yarik and Musical Notes
    思路暴力化简公式题。假定\(b_{i}^{b_j}=b_{j}^{b_{i}}\)成立,那么有:\[2^{a_i\times2^{a_j}}=2^{a_j\times2^{a_i}}\\a_i\times2^{a_j}=a_j\times2^{a_i}\\\frac{a_i}{a_j}=\frac{2^{a_i}}{2^{a_j}}\\\frac{a_i}{a_j}=2^{a_i-a_j}\]因为\(\fra......
  • CF1898 C Colorful Grid 题解
    LinkCF1898CColorfulGridQuestion给出一个\(N\timesM\)的网格图给每一条边染色(R/B),需要存在一条长度为\(K\)的路径从\((1,1)\)到\((N,M)\),路径允许重复通过一个节点。Solution非常有意思的一道题先考虑\(K\)满足的最小值,显然是\((N-1)+(M-1)\),假设走上->......
  • CF1898 B Milena and Admirer 题解
    LinkCF1898BMilenaandAdmirerQuestion给出一个长度为\(n\)的序列\(a\),我们可以做一种操作使得\(a\)非降,操作是:对于一个\(a_i\)选择一个整数\(0\lex\lea_i\),用两个数\(x,(a_i-x)\)来替换\(a_i\)。求最小操作次数。Solution考虑哪些数是需要操作的,如......
  • CF1899 G Unusual Entertainment 题解
    LinkCF1899GUnusualEntertainmentQuestion给出一个排列\(p_i\)和一棵树,给出\(Q\)组询问,每组询问\([L,R,x]\)表示求\(p_L\simp_R\)上是否存在\(p_i\)在\(x\)的字数上。Solution这道题确实是一个好题。我们先考虑一个问题,怎么样才能判断子树,我们给书上的每个......
  • Windows端口占用问题解决
    查询被占用的端口进程8080为端口号netstat-ano|findstr8080杀掉线程14816为进程号taskkill/f/t/im14816......