题解:P12003 在小小的奶龙山里面挖呀挖呀挖(加强版)
这是一篇空间上真正理解出题人意图的题解,具体表现在利用得很充分。
正文
我们都知道对于一个数 \(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_;
}
}
// 星間~ 干渉~ 融解~ 輪迴~ 邂逅~ 再生~ ララバイ~

浙公网安备 33010602011771号