Y
K
N
U
F

题解:P12003 在小小的奶龙山里面挖呀挖呀挖(加强版)

link

这是一篇空间上真正理解出题人意图的题解,具体表现在利用得很充分。

\(949.64\) MB

正文

我们都知道对于一个数 \(a\) 最多有有 \(1\) 个大于 \(\sqrt a\) 的质因数,大概可以想到根号分治,把一个数的质因数按照 \(\sqrt V\) 分为两个部分。

首先考虑小于等于 \(\sqrt V\) 的部分,我们发现这个和弱化版的值域就很像了,选择和弱化版类似的做法,bitset 维护一个数的质因数集,然后用树链剖分维护链上的质因数集,这里如果使用线段树可以做到 \(O(n \pi (\sqrt V) + q\log ^2n \frac {\pi(\sqrt V)} {w})\) 的复杂度,我们可以发现这两部分根本不平衡,所以考虑平衡一下,注意到根本没有修改操作,把线段树改成 st 表可以优化掉一个 \(\log\) 变成 \(O((n + q)\log n \frac {\pi(\sqrt V)} {w})\)。假设我们未卜先知得到大于 \(\sqrt V\) 的部分的复杂度为 \(q \sqrt n\),与之相比咱这个还是不怎么平衡,进一步优化可以考虑更改分治边界为 \(V ^{\frac 13}\),多留给第二部分一个质因数,可以得到复杂度为 \(O((n + q)\log n \frac {\pi(V^{\frac 13})} {w})\),然而实际上会更慢就是了。

然后是大于 \(\sqrt V\) 的部分,把这些质因数离散化,我们发现这就相当于一个树上的数颜色问题,可以很简单的用树上莫队来做,注意维护好边界,以及不要把 \(1\) 算上就行。

总时间复杂度 \(O((n + q)\log n \frac {\pi({\sqrt V})} {w} + q\sqrt n)\),空间复杂度 \(O(开的下)\)

代码如下:

// code by 樓影沫瞬_Hz17
#include <bits/stdc++.h>
using namespace std;
 
#define getc() getchar_unlocked()
#define putc(a) putchar_unlocked(a)
#define en_ putc('\n')
#define e_ putc(' ')

using pii = pair<int, int>;
 
template<class T> inline T in() { 
	T n = 0; char p = getc();
	while(p < '-') p = getc();
	bool f = p == '-' ? p = getc() : 0;
	do n = n * 10 + (p ^ 48), p = getc();
	while(isdigit(p));
	return f ? -n : n;
}
template<class T> inline T in(T &a) { return a = in<T>(); }
template<class T, class ... Args> inline void in(T &t, Args&... args) { in(t), in(args...); }
 
template<class T> inline void out(T n) {
	if(n < 0) putc('-'), n = -n;
	if(n > 9) out(n / 10);
	putc(n % 10 + '0');
}
 
template<class T1, class T2> T1 max(T1 a, T2 b) { return a > b ? a : a = b;}
template<class T1, class T2> T1 min(T1 a, T2 b) { return a < b ? a : a = b;}
 
constexpr int N = 3e5 + 10, V = 1e8, B = 780;

int Q, n;

bitset<10000 + 10> vis;
int pri[4000], cnp;

bitset<1240> f[19][N];

int a[N], dfn[N], rdfn[N], top[N], fa[N], sz[N], wc[N], dep[N];

inline void Hash(int *l, int *r) { // 离散化
	vector<int> v{1};
	for(int *i = l; i != r; i ++) v.push_back(*i);
	sort(v.begin(), v.end()), v.erase(unique(v.begin(), v.end()), v.end());
	for(int *i = l; i != r; i ++) 
		*i = lower_bound(v.begin(), v.end(), *i) - v.begin() + 1;
}

inline void buildst() { // 建 st 表,同时顺便把 a 变成 a 的大质因数
	for(int i = 1; i <= n; i ++) {
		for(int j = 1; j <= cnp; j ++) {
			if(a[rdfn[i]] == 1) break;
			if(a[rdfn[i]] % pri[j] == 0) {
				while(a[rdfn[i]] % pri[j] == 0) a[rdfn[i]] /= pri[j];
				f[0][i][j] = 1; 
			}
		}
	}

	for(int i = 1; i <= 18; i ++) 
		for(int j = 1; j <= n; j ++) 
			f[i][j] = f[i - 1][j] | f[i - 1][min(n, j + (1 << (i - 1)))];
}

int lg2[N];

inline bitset<1240> query(int l, int r) { // st 查
	int k = lg2[r - l + 1];
	return f[k][l] | f[k][r - (1 << k) + 1];
}

int pos[N * 2];

inline void pre() { // 预处理
	for(int i = 2; i <= 10000; i ++) {
		if(!vis[i]) pri[++cnp] = i;
		for(int j = 1; j <= cnp and pri[j] * i <= 10000; j ++) {
			vis[pri[j] * i] = 1;
			if(i % pri[j] == 0) break;
		}
	}
	for(int i = 2; i <= n; i ++) lg2[i] = lg2[i >> 1] + 1;
	for(int i = 1; i <= n * 2; i ++) pos[i] = i / B;
}	

vector<int> e[N];
int st[N], ed[N], rr[N * 2], tim;

inline void dfs(int u, int f) { // 同时处理两个序
	st[u] = ++tim, rr[tim] = u; 
	dep[u] = dep[f] + 1, fa[u] = f, sz[u] = 1;
	for(int v : e[u]) {
		if(v == f) continue;
		dfs(v, u);
		sz[u] += sz[v];
		wc[u] = sz[wc[u]] > sz[v] ? wc[u] : v;
	}
	ed[u] = ++tim, rr[tim] = u;
}

int idfn;

inline void dps(int u, int tp) {
	top[u] = tp, dfn[u] = ++ idfn, rdfn[idfn] = u;
	if(wc[u]) dps(wc[u], tp);
	for(int v : e[u]) 
		if(v != fa[u] and v != wc[u]) dps(v, v);
}

inline int que(int a, int b) { // 树剖查询
	bitset<1240> res; res.reset();
	while(top[a] != top[b]) {
		if(dep[top[a]] < dep[top[b]]) swap(a, b);
		res |= query(dfn[top[a]], dfn[a]);
		a = fa[top[a]];
	}
	if(dfn[a] < dfn[b]) swap(a, b);
	res |= query(dfn[b], dfn[a]);
	return res.count();
}

inline int lca(int a, int b) { 
	while(top[a] != top[b]) {
		if(dep[top[a]] < dep[top[b]]) swap(a, b);
		a = fa[top[a]];
	}
	return dep[a] < dep[b] ? a : b;
}

int fig[N], cnt[N], ans[N];

struct qry {
	int l, r, lca, id;
} q[N];
 
signed main() {
	#ifndef ONLINE_JUDGE
		freopen("in.ru", "r", stdin);
		freopen("out.ru", "w", stdout);
	#endif
	in(n, Q);

	pre();

	for(int i = 1; i <= n; i ++) in(a[i]);
	
	for(int u, v, i = 1; i < n; i ++) {
		in(u, v);
		e[u].push_back(v);
		e[v].push_back(u);
	}

	dfs(1, 1);
	dps(1, 1);
	buildst();
	Hash(a + 1, a + 1 + n);

	for(int i = 1, u, v; i <= Q; i ++) {
		in(u, v);
		if(st[u] > st[v]) swap(u, v);
		ans[i] = que(u, v);
		int l = lca(u, v);
		if(l == u) q[i] = {st[u], st[v], 0, i};
		else q[i] = {ed[u], st[v], l, i};
	}
	
	sort(q + 1, q + 1 + Q, [](qry a, qry b) {return pos[a.l] == pos[b.l] ? pos[a.l] & 1 ? a.r < b.r : a.r > b.r : pos[a.l] < pos[b.l];});

	int l = 1, r = 0, sum = 0;

	for(int i = 1; i <= Q; i ++) {
		while(l < q[i].l) {
			fig[rr[l]] --;
			if(fig[rr[l]] == 1) if(a[rr[l]] != 1) sum += !cnt[a[rr[l]]] ++;
			if(fig[rr[l]] == 0) if(a[rr[l]] != 1) sum -= !-- cnt[a[rr[l]]];
			l ++;
		}
		while(l > q[i].l) {
			l --;
			fig[rr[l]] ++;
			if(fig[rr[l]] == 1) if(a[rr[l]] != 1) sum += !cnt[a[rr[l]]] ++;
			if(fig[rr[l]] == 2) if(a[rr[l]] != 1) sum -= !-- cnt[a[rr[l]]];
		}
		while(r < q[i].r) {
			r ++;
			fig[rr[r]] ++;
			if(fig[rr[r]] == 1) if(a[rr[r]] != 1) sum += !cnt[a[rr[r]]] ++;
			if(fig[rr[r]] == 2) if(a[rr[r]] != 1) sum -= !-- cnt[a[rr[r]]];
		}
		while(r > q[i].r) {
			fig[rr[r]] --;
			if(fig[rr[r]] == 1) if(a[rr[r]] != 1) sum += !cnt[a[rr[r]]] ++;
			if(fig[rr[r]] == 0) if(a[rr[r]] != 1) sum -= !-- cnt[a[rr[r]]];
			r --;
		}
		if(q[i].lca) if(a[q[i].lca] != 1) sum += !cnt[a[q[i].lca]] ++;
		ans[q[i].id] += sum;
		if(q[i].lca) if(a[q[i].lca] != 1) sum -= !-- cnt[a[q[i].lca]];
	}	

	for(int i = 1; i <= Q; i ++) {
		out(ans[i]), en_;
	}
}   
// 星間~ 干渉~ 融解~ 輪迴~ 邂逅~ 再生~ ララバイ~
posted @ 2025-10-19 19:11  樓影沫瞬_Hz17  阅读(12)  评论(0)    收藏  举报