题意
有一颗 个节点的树,每个点有权值
,
这条边的权值为
。有
次操作:
将节点
的值改为
询问
到
的路径上有多少条边边权不超过
其中,,
。
分析
首先看到这题时限 ,果断上 O(1)gcd + 暴力,复杂度
,结果没卡过去。。。
不过为什么其他人的 做法都能过啊,我的就不能过QAQ
下面的解法学习自 CYJian大佬的博客。Orz
先以 为根节点,进行树链剖分。(如果你不会树剖,可以先去看看 这道模板题
然后每次修改节点 的值,先只对重链上的边进行修改。
这样子就保证了重链上的边都是正确值。
然后在跳链时,再更新重链之间的连接边(轻链)的边权即可。
修改边权 + 查询不超过 的边的个数可以使用动态修改的主席树。(如果你不会动态修改主席树,可以先去看看这道模板题,
我也是现学的
复杂度的话,由于每次只会跳 次,每次跳更新和求和的时间复杂度都是
。
所以总的复杂度是 。
最关键的点是每次只修改重链,遇到轻链再修改轻链,这样做的原因大概是树剖在跳的过程中是一直在沿着重链往上跳,最多会遇到 条轻链,不知道是不是套路,不过感觉还是挺巧妙的QAQ。
代码如下
#include <bits/stdc++.h>
#define lson l, m, lch[rt]
#define rson m + 1, r, rch[rt]
#define N 20005
using namespace std;
typedef long long LL;
LL z = 1;
int read(){
int x, f = 1;
char ch;
while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') f = -1;
x = ch - '0';
while(ch = getchar(), ch >= '0' && ch <= '9') x = x * 10 + ch - 48;
return x * f;
}
struct node{
int a, b, c, n;
}d[N * 2];
int n, dep[N], re[N], siz[N], son[N], fa[N], top[N], h[N], v[N], w[N], dfn[N], dft, cnt, maxn = 1000000;
int cur1, cur2, L[35], R[35], ret, root[N], sum[N * 400], lch[N * 400], rch[N * 400];
void update(int l, int r, int &rt, int p, int c){
if(!rt) rt = ++ret;
sum[rt] += c;
if(l == r) return;
int m = l + r >> 1;
if(p <= m) update(lson, p, c);
else update(rson, p, c);
}
int query(int l, int r, int k){
int i, m = l + r >> 1;
LL siz = 0;
if(l == r){
for(i = 1; i <= cur1; i++) siz -= sum[L[i]];
for(i = 1; i <= cur2; i++) siz += sum[R[i]];
return siz;
}
if(k <= m){
for(i = 1; i <= cur1; i++) L[i] = lch[L[i]];
for(i = 1; i <= cur2; i++) R[i] = lch[R[i]];
return query(l, m, k);
}
else{
for(i = 1; i <= cur1; i++) siz -= sum[lch[L[i]]], L[i] = rch[L[i]];
for(i = 1; i <= cur2; i++) siz += sum[lch[R[i]]], R[i] = rch[R[i]];
return siz + query(m + 1, r, k);
}
}
int gsum(int l, int r, int k){
cur1 = cur2 = 0;
for(int i = l - 1; i > 0; i -= i & -i) L[++cur1] = root[i];
for(int i = r; i > 0; i -= i & -i) R[++cur2] = root[i];
return query(0, maxn, k);
}
void add(int x, int v, int c){
for(int i = x; i <= n; i += i & -i) update(0, maxn, root[i], v, c);
}
void cr(int a, int b){
d[++cnt].a = a; d[cnt].b = b; d[cnt].n = h[a]; h[a] = cnt;
}
void dfs1(int a){
int i, b;
siz[a] = 1;
for(i = h[a]; i; i = d[i].n){
b = d[i].b;
if(b == fa[a]) continue;
fa[b] = a;
dep[b] = dep[a] + 1;
w[b] = __gcd(v[b], v[a]);
dfs1(b);
siz[a] += siz[b];
if(siz[b] >= siz[son[a]]) son[a] = b;
}
}
void dfs2(int a, int f){
int i, b;
top[a] = f; dfn[a] = ++dft;
if(son[a]) dfs2(son[a], f);
for(i = h[a]; i; i = d[i].n){
b = d[i].b;
if(b != fa[a] && b != son[a]) dfs2(b, b);
}
}
void modify(int a){
if(son[a]){
int b = son[a];
add(dfn[b], w[b], -1);
w[b] = __gcd(v[a], v[b]);
add(dfn[b], w[b], 1);
}
if(fa[a]){
int b = fa[a];
add(dfn[a], w[a], -1);
w[a] = __gcd(v[a], v[b]);
add(dfn[a], w[a], 1);
}
}
int find(int a, int b, int c){
int s = 0, f1 = top[a], f2 = top[b];
while(f1 != f2){
if(dep[f1] < dep[f2]) swap(f1, f2), swap(a, b);
add(dfn[f1], w[f1], -1);
w[f1] = __gcd(v[f1], v[fa[f1]]);
add(dfn[f1], w[f1], 1);
s += gsum(dfn[f1], dfn[a], c);
a = fa[f1], f1 = top[a];
}
if(dep[a] > dep[b]) swap(a, b);
if(a != b) s += gsum(dfn[a] + 1, dfn[b], c);
return s;
}
int main(){
int i, j, m, o, a, b, c;
n = read(); m = read();
for(i = 1; i <= n; i++) v[i] = read();
for(i = 1; i < n; i++){
a = read(); b = read();
cr(a, b); cr(b, a);
}
dfs1(1);
dfs2(1, 1);
for(i = 1; i <= n; i++) add(dfn[i], w[i], 1);
for(i = 1; i <= m; i++){
o = read(); a = read(); b = read();
if(o == 1){
v[a] = b;
modify(a);
}
else{
c = read();
printf("%d\n", find(a, b, c));
}
}
return 0;
}
京公网安备 11010502036488号