题意

有一颗 个节点的树,每个点有权值 这条边的权值为 。有 次操作:

  • 将节点 的值改为
  • 询问 的路径上有多少条边边权不超过

其中,

分析

首先看到这题时限 ,果断上 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;
}