[CC]Dynamic GCD

题意简述

一棵 n 个点的带权树。F询问一条路径上所有点的gcd,或C将一条路径上所有点权+d

数据范围

1n50000
1ai,d1000

思路

如果维护gcd很难维护。
gcd(x,y)=gcd(x,xy)
我们可以维护差分序列,将区间加转化为单点操作。
每次询问,起始端是原数,后面是差分数列。
再用树状数组维护一下原数。
复杂度 O(nlog3n)

代码

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
using namespace std;
#define MAXN 50010
struct edge{
    int s,t,next;
}e[MAXN<<1];
int head[MAXN],cnt;
void addedge(int s,int t)
{
    e[cnt].s=s;e[cnt].t=t;e[cnt].next=head[s];head[s]=cnt++;
    e[cnt].s=t;e[cnt].t=s;e[cnt].next=head[t];head[t]=cnt++;
}
int n,ord,u,v,w,q;
char opt;
int deep[MAXN],fa[MAXN],size[MAXN],son[MAXN],top[MAXN],dfn[MAXN],seq[MAXN],a[MAXN];
void dfs1(int node,int lastfa,int de)
{
    deep[node]=de;
    fa[node]=lastfa;
    size[node]=1;
    son[node]=0;
    for (int i=head[node];i!=-1;i=e[i].next)
        if (e[i].t!=lastfa)
        {
            dfs1(e[i].t,node,de+1);
            size[node]+=size[e[i].t];
            if (size[e[i].t]>size[son[node]])
                son[node]=e[i].t;
        }
}
void dfs2(int node,int lastfa,int tp)
{
    top[node]=tp;
    dfn[node]=++ord;
    seq[ord]=a[node];
    if (son[node])
        dfs2(son[node],node,tp);
    for (int i=head[node];i!=-1;i=e[i].next)
        if (e[i].t!=lastfa&&e[i].t!=son[node])
            dfs2(e[i].t,node,e[i].t);
}
int gcd(int a,int b)
{
    if (a<0||b<0)
        return gcd(abs(a),abs(b));
    if (!b)
        return a;
    return gcd(b,a%b);
}
namespace Segtree
{
    struct Node{
        int val;
    }tree[MAXN<<2];
    int pushup(int node)
    {
        tree[node].val=gcd(tree[node<<1].val,tree[node<<1|1].val);
    }
    void build(int l,int r,int node)
    {
        if (l==r)
        {
            tree[node].val=seq[l];
            return;
        }
        int mid=(l+r)>>1;
        build(l,mid,node<<1);
        build(mid+1,r,node<<1|1);
        pushup(node);
    }
    void modify(int pos,int l,int r,int node,int w)
    {
        if (l==r)
        {
            tree[node].val+=w;
            return;
        }
        int mid=(l+r)>>1;
        if (pos<=mid)
            modify(pos,l,mid,node<<1,w);
        else
            modify(pos,mid+1,r,node<<1|1,w);
        pushup(node);
    }
    int query(int L,int R,int l,int r,int node)
    {
        if (L<=l&&r<=R)
            return tree[node].val;
        int mid=(l+r)>>1;
        int ret=0;
        if (L<=mid)
            ret=gcd(ret,query(L,R,l,mid,node<<1));
        if (R>mid)
            ret=gcd(ret,query(L,R,mid+1,r,node<<1|1));
        return ret;
    }
}
struct BIT{
    static const int size=50000;
    int d[50010];
    int lowbit(int x)
    {
        return x&(-x);
    }
    void modify(int l,int r,int val)
    {
        for (;l<=size;l+=lowbit(l))
            d[l]+=val;
        for (++r;r<=size;r+=lowbit(r))
            d[r]-=val;
    }
    int query(int pos)
    {
        int ret=0;
        for (;pos;pos-=lowbit(pos))
            ret+=d[pos];
        return ret;
    }
}T;
void solve_modify(int u,int v,int w)
{
    int tu=top[u],tv=top[v],ret=0;
    while (tu!=tv)
    {
        if (deep[tu]<deep[tv])
            swap(u,v),swap(tu,tv);
        T.modify(dfn[tu],dfn[u],w);
        Segtree::modify(dfn[tu],1,n,1,w);
        if (dfn[u]+1<=n)
            Segtree::modify(dfn[u]+1,1,n,1,-w); 
        u=fa[tu];
        tu=top[u];
    }
    if (deep[u]>deep[v])
        swap(u,v);
    T.modify(dfn[u],dfn[v],w);
    Segtree::modify(dfn[u],1,n,1,w);
    if (dfn[v]+1<=n)
        Segtree::modify(dfn[v]+1,1,n,1,-w);
}
int solve_query(int u,int v)
{
    int tu=top[u],tv=top[v],ret=0;
    while (tu!=tv)
    {
        if (deep[tu]<deep[tv])
            swap(u,v),swap(tu,tv);
        ret=gcd(ret,T.query(dfn[tu]));
        if (dfn[tu]+1<=dfn[u])
            ret=gcd(ret,Segtree::query(dfn[tu]+1,dfn[u],1,n,1));
        u=fa[tu];
        tu=top[u];
    }
    if (deep[u]>deep[v])
        swap(u,v);
    ret=gcd(ret,T.query(dfn[u]));
    if (dfn[u]+1<=dfn[v])
        ret=gcd(ret,Segtree::query(dfn[u]+1,dfn[v],1,n,1));
    return ret;
}
char read()
{
    char ch=getchar();
    while (ch!='F'&&ch!='C')
        ch=getchar();
    return ch;
}
int main()
{
    scanf("%d",&n);
    memset(head,0xff,sizeof(head));
    cnt=0;
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        u++,v++;
        addedge(u,v);
    }
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    dfs1(1,1,1);
    dfs2(1,1,1);
    for (int i=1;i<=n;i++)
        T.modify(i,i,seq[i]);
    for (int i=n;i>=1;i--)
        seq[i]-=seq[i-1];
    Segtree::build(1,n,1);
    scanf("%d",&q);
    for (int i=1;i<=q;i++)
    {
        opt=read();
        if (opt=='F')
        {
            scanf("%d%d",&u,&v);
            u++,v++;
            printf("%d\n",solve_query(u,v));
        }
        else
        {
            scanf("%d%d%d",&u,&v,&w);
            u++,v++;
            solve_modify(u,v,w);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值