题目描述
给定一棵树,树带点权,树的边权等于边两端点权的 。
有两种操作 :
更改一个点的点权,同时与之相连的边权也跟着改变。
询问两个点的链上边权小于等于
的个数。
正解
先将一下复杂度吧 ,挺暴力的。
考虑根号分治。
修改 :
对于一个度数大于 的点,修改时只修改点权,忽视其边权的贡献(查询时再暴力求
计算贡献) 。
对于一个度数小于 的点,暴力修改其连出去的边权。
修改时复杂度瓶颈在度数小于 的点,时间复杂度为
。
查询 :
查询的时候暴力跳 统计答案,然后对于度数大于
的点,还要求
。
但是度数大于 的点不会超过
个,查询总复杂度为
。
代码
#include <bits/stdc++.h>
using namespace std;
const int inf = 1e8;
const int N = 20005;
const int B = 150;
int n, q;
int deg[N], dep[N];
int a[N], fa[N], fe[N];
vector<int> g[N];
inline int gcd(int x, int y) { return y ? gcd(y, x % y) : x; }
inline int read() {
int x = 0; char ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
return x;
}
void preDfs(int u) {
dep[u] = dep[fa[u]] + 1;
for(int v : g[u]) {
if(v == fa[u]) continue;
fa[v] = u, fe[v] = (deg[u] > B || deg[v] > B ? inf : gcd(a[u], a[v]));
preDfs(v);
}
}
int main() {
n = read(), q = read();
for(int i = 1; i <= n; ++i) a[i] = read();
for(int i = 1, u, v; i < n; ++i) {
u = read(), v = read();
++deg[u], ++deg[v];
g[u].push_back(v), ++deg[u];
g[v].push_back(u), ++deg[v];
}
preDfs(1);
for(int i = 1, opt, u, v, x; i <= q; ++i) {
opt = read(), u = read(), v = read();
if(opt == 1) {
a[u] = v;
if(deg[u] > B) continue;
for(int v : g[u]) {
if(deg[v] > B) continue;
if(fa[u] == v) {
fe[u] = gcd(a[u], a[v]);
} else {
fe[v] = gcd(a[u], a[v]);
}
}
} else {
x = read();
int ans = 0;
if(dep[u] < dep[v]) swap(u, v);
while(dep[u] > dep[v]) {
ans += (fe[u] <= x);
if(deg[u] > B || deg[fa[u]] > B)
ans += (gcd(a[u], a[fa[u]]) <= x);
u = fa[u];
}
if(u == v) goto print;
while(u != v) {
ans += (fe[u] <= x);
if(deg[u] > B || deg[fa[u]] > B)
ans += (gcd(a[u], a[fa[u]]) <= x);
u = fa[u];
ans += (fe[v] <= x);
if(deg[v] > B || deg[fa[v]] > B)
ans += (gcd(a[v], a[fa[v]]) <= x);
v = fa[v];
}
print :
printf("%d\n", ans);
}
}
return 0;
} 
京公网安备 11010502036488号