首页 > 其他分享 >UOJ33 树上 GCD

UOJ33 树上 GCD

时间:2023-09-05 14:35:03浏览次数:39  
标签:UOJ33 typedef GCD int long maxn mxd 树上 define

UOJ 传送门

设 \(f_{u, i}\) 为 \(u\) 子树内深度为 \(i\) 的点的个数,在 \(\operatorname{LCA}\) 处计算答案。但是时间复杂度无法接受。

考虑长剖,计算答案只用枚举到轻链长,先对轻儿子做一遍 \(\text{Dirichlet}\) 后缀和,重儿子的信息直接继承上来。但是我们没法查询深度 \(\bmod k = i\) 的点的个数。

这是经典根号分治,\(\le \sqrt{n}\) 的加入时处理好,\(> \sqrt{n}\) 的暴力枚举。

然后总时间复杂度是 \(O(n \sqrt{n})\) 的。

别忘记计算点对是祖先后代关系的点。

code
// Problem: #33. 【UR #2】树上GCD
// Contest: UOJ
// URL: https://uoj.ac/problem/33
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 200100;
const int B = 300;

int n, pr[maxn], tot;
ll g[maxn], h[maxn];
vector<int> G[maxn];
bool vis[maxn];

int dep[maxn], mxd[maxn], son[maxn];

void dfs(int u) {
	mxd[u] = dep[u];
	for (int v : G[u]) {
		dep[v] = dep[u] + 1;
		dfs(v);
		if (mxd[v] > mxd[u]) {
			mxd[u] = mxd[v];
			son[u] = v;
		}
	}
}

vector<ll> f[maxn];

namespace DS {
	ll f[B + 5][B + 5], g[maxn];
	
	inline void add(int x, ll y) {
		g[x] += y;
		for (int i = 1; i <= B; ++i) {
			f[i][x % i] += y;
		}
	}
	
	inline void clear(int x) {
		g[x] = 0;
		for (int i = 1; i <= B; ++i) {
			f[i][x % i] = 0;
		}
	}
	
	inline ll query(int k, int x) {
		return f[k][x % k];
	}
}

void dfs2(int u) {
	for (int v : G[u]) {
		if (v == son[u]) {
			continue;
		}
		dfs2(v);
		f[v] = vector<ll>(1, 0);
		for (int i = dep[v]; i <= mxd[v]; ++i) {
			f[v].pb(DS::g[i]);
			DS::clear(i);
		}
	}
	if (son[u]) {
		dfs2(son[u]);
	}
	for (int v : G[u]) {
		if (v == son[u]) {
			continue;
		}
		int m = (int)f[v].size() - 1;
		for (int i = 1; i <= tot && pr[i] <= m; ++i) {
			for (int j = m / pr[i]; j; --j) {
				f[v][j] += f[v][pr[i] * j];
			}
		}
		for (int i = 1; i <= m; ++i) {
			if (i <= B) {
				g[i] += f[v][i] * DS::query(i, dep[u]);
			} else {
				ll s = 0;
				for (int j = dep[u]; j <= mxd[u]; j += i) {
					s += DS::g[j];
				}
				g[i] += f[v][i] * s;
			}
		}
		for (int i = 1; i <= tot && pr[i] <= m; ++i) {
			for (int j = 1; j <= m / pr[i]; ++j) {
				f[v][j] -= f[v][pr[i] * j];
			}
		}
		for (int i = 1; i <= m; ++i) {
			DS::add(dep[u] + i, f[v][i]);
		}
	}
	DS::add(dep[u], 1);
}

void solve() {
	scanf("%d", &n);
	for (int i = 2; i <= n; ++i) {
		if (!vis[i]) {
			pr[++tot] = i;
		}
		for (int j = 1; j <= tot && i * pr[j] <= n; ++j) {
			vis[i * pr[j]] = 1;
			if (i % pr[j] == 0) {
				break;
			}
		}
	}
	for (int i = 2, p; i <= n; ++i) {
		scanf("%d", &p);
		G[p].pb(i);
	}
	dfs(1);
	dfs2(1);
	for (int i = 1; i <= tot && pr[i] <= mxd[1]; ++i) {
		for (int j = 1; j * pr[i] <= mxd[1]; ++j) {
			g[j] -= g[j * pr[i]];
		}
	}
	for (int i = 1; i < n; ++i) {
		h[1] += DS::g[i];
		h[i + 1] -= DS::g[i];
	}
	for (int i = 1; i < n; ++i) {
		h[i] += h[i - 1];
		printf("%lld\n", g[i] + h[i]);
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

标签:UOJ33,typedef,GCD,int,long,maxn,mxd,树上,define
From: https://www.cnblogs.com/zltzlt-blog/p/17679456.html

相关文章

  • 关于exgcd的总结
    关于exgcd的总结我们主要讨论的是\(ax+by=c\)1.exgcd算法1.1关于解的存在性有裴蜀定理知,对于方程\(ax+by=c\)存在解的充分必要条件是:\((a,b)|c\)tips:裴蜀定理如果\(a,b\)均为整数,则有整数\(x,y\)使得\(ax+by=\gcd(a,b)\),这个等式称为裴蜀等式。1.2exgcd算法介绍要求\(a......
  • GCD Counting题解
    题意有一棵有\(n\)个节点的树,第\(i\)个节点有点权\(a_i\)。定义\(g(x,y)\)为\(x\)到\(y\)的树上路径所经过的点的点权\(\gcd\)。对于每一个正整数\(k\in[1,2\times10^5]\)求出满足以下条件的\(x,y\)的对数:\(1\lex\ley\len\)\(g(x,y)=k\)\(1\len......
  • 第 360 场周赛 (二进制枚举、树上倍增)
    2833. 距离原点最远的点 本题要求最远的距离,所有‘_’必须全为左或全为右。利用前缀和的思想看看L多还是R多,最后加上_的数目就是答案。classSolution{public:intfurthestDistanceFromOrigin(stringmoves){intn=moves.size();inttt=0,l......
  • 2023牛客暑期多校练营6 A-Tree 树上背包+并查集
    2023牛客暑期多校练营6A-Tree树上背包+并查集题目链接题意:给出一棵树,节点为黑色或者白色,定义整棵树的贡献为,任意白点到任意黑点所经过路径上的最大边权之和,节点i原本颜色已给出,可以花费c[i]代价翻转节点i的颜色,问最大贡献是多少。做法:首先我们思考怎么处理最大边权的问题......
  • CF979D Kuro and GCD and XOR and SUM
    题目大意初始有一个空的集合,和\(Q\)个操作。对于每个操作,有两种类型,分别用如下的两种形式表示:1u:加入\(u\)到集合2xks:求一个最大的\(v\),使得:\(v+x\leqs\)\(k\mid\gcd(v,x)\)\(x\oplusv\)最大(其中\(\oplus\)表示按位异或,对应C++中的^运算符)如果找不......
  • 树上可持久化线段树
    例题传送门:Countonatree简要题意:有棵\(n\)个节点的树,每次点有个权值\(a_i\),每次询问给出\(u,v,k\),求\(u,v\)两个节点的简单路径上(包括\(u,v\))上第\(k\)小的点,保证数据有解,强制在线\(1\len,m\le10^5,a_i\in[1,2^{31}-1]\)首先,第\(k\)小就可以想到要可持久化线段树,动态开......
  • 树上启发式合并
    与其说树上启发式合并是一种算法,不如说是一种思想。它在于通过”小的并入大的“保证复杂度,从而解决很多看似无法做的问题。论纯用树上启发式合并的题很少,但是很多题却可以用树上启发式合并去解决。模板求解的问题往往具有如下性质:每颗子树都有要记录的信息,信息的数量和子树大......
  • 树上启发式合并
    树上启发式合并与其说树上启发式合并是一种算法,不如说是一种思想。它在于通过”小的并入大的“保证复杂度,从而解决很多看似无法做的问题。论纯用树上启发式合并的题很少,但是很多题却可以用树上启发式合并去解决。模板求解的问题往往具有如下性质:每颗子树都有要记录的信息,信......
  • 树上背包优化 - 时间复杂度证明
    树上背包优化-时间复杂度证明例题树上背包顾名思义,就是在树上做背包dp树上背包的模板代码如下voiddfs(intx){ sz[x]=1; if(x>=n-m+1){ dp[x][1]=-a[x]; return; } for(PIIe:eg[x]){ intnxt=e.first; dfs(nxt); sz[x]+=sz[nxt]; for......
  • P1612 [yLOI2018] 树上的链
    因为自己太憨了,所以交了好几次都没过,谢谢审核大大!!!思路因为这是一棵树,所以每个节点只有一个父亲,那么选定一个结点,它到根节点的路径唯一。所以第一个思路就是暴力,对于每一个节点,直接暴力向上枚举,找到第一个满足条件的节点,然后输出长度即可。但是显然,第一种方法很容易TLE,所以我......