CodeChef DGCD Dynamic GCD

177 篇文章 0 订阅
148 篇文章 0 订阅

You’re given a tree on N vertices. Each vertex has a positive integer
written on it, number on the ith vertex being vi. Your program must
process two types of queries :

  1. Find query represented by F u v : Find out gcd of all numbers on the unique path between vertices u and v in the tree (both inclusive).

  2. Change query represented by C u v d : Add d to the number written on all vertices along the unique path between vertices u and v in the
    tree (both inclusive). Input

First line of input contains an integer N denoting the size of the
vertex set of the tree. Then follow N - 1 lines, ith of which contains
two integers ai and bi denoting an edge between vertices ai and bi in
the tree. After this follow N space separated integers in a single
line denoting initial values vi at each of these nodes. Then follows a
single integer Q on a line by itself, denoting the number of queries
to follow. Then follow Q queries, each one on a line by itself. Each
query is either a find query or a change query with format as given in
problem statement. Note that all vertices are 0-based. Output For
every find query, print the answer to that query in one line by
itself.

很明显用树剖,关键是线段树上如何维护GCD。
直接维护的话无法进行区间修改区间查询,但是考虑到辗转相减和差分,可以维护两个线段树,一个保存原序列【不妨看成原树的点】的值,进行区间修改单点查询,另一个保存差分序列【不妨看成原树的边】的GCD,进行单点修改区间查询。询问区间就是差分序列的GCD和原序列某一个数【为了方便取首或尾】的GCD,修改就是在差分序列上单点修改,原序列上打标记。
一个需要注意的细节就是在差分序列上单点修改的时候,改变一个端点的值会让他所有连边的值发生改变,如果全都修改的话复杂度就无法保证。所以我们只维护重边上的差分值,询问的时候用单点查询来代替轻边查询。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int fir[50010],ne[100010],to[100010],v[50010],
size[50010],fa[50010],son[50010],dep[50010],
pos[50010],top[50010],
tag[500010],val1[500010],gcd2[500010],
m,n,tot;
int abs(int x)
{
    return x>0?x:-x;
}
int gcd(int x,int y)
{
    return y?gcd(y,x%y):abs(x);
}
void add(int num,int u,int v)
{
    ne[num]=fir[u];
    fir[u]=num;
    to[num]=v;
}
void init()
{
    int i,x,y;
    scanf("%d",&n);
    for (i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        x++;
        y++;
        add(i*2,x,y);
        add(i*2+1,y,x);
    }
    for (i=1;i<=n;i++)
      scanf("%d",&v[i]);
}
void dfs1(int u)
{
    int i,v;
    size[u]=1;
    for (i=fir[u];i;i=ne[i])
      if ((v=to[i])!=fa[u])
      {
        fa[v]=u;
        dep[v]=dep[u]+1;
        dfs1(v);
        size[u]+=size[v];
        if (son[u]==0||size[v]>size[son[u]])
          son[u]=v;
      }
}
void dfs2(int u)
{
    int i,v;
    pos[u]=++tot;
    if (!son[u]) return;
    top[son[u]]=top[u];
    dfs2(son[u]);
    for (i=fir[u];i;i=ne[i])
      if ((v=to[i])!=fa[u]&&v!=son[u])
      {
        top[v]=v;
        dfs2(v);
      }
}
void down1(int p,bool is)
{
    if (is)
      val1[p]+=tag[p];
    else
    {
        tag[p*2]+=tag[p];
        tag[p*2+1]+=tag[p];
    }
    tag[p]=0;
}
void m1(int p,int L,int R,int l,int r,int x)
{
    down1(p,L==R);
    int mid=(L+R)/2;
    if (l<=L&&R<=r)
    {
        tag[p]=x;
        return;
    }
    if (l<=mid) m1(p*2,L,mid,l,r,x);
    if (r>=mid+1) m1(p*2+1,mid+1,R,l,r,x);
}
int q1(int p,int L,int R,int k)
{
    down1(p,L==R);
    if (L==R) return val1[p];
    int mid=(L+R)/2;
    if (k<=mid) return q1(p*2,L,mid,k);
    return q1(p*2+1,mid+1,R,k);
}
void m2(int p,int L,int R,int k,int x)
{
    if (L==R)
    {
        gcd2[p]+=x;
        return;
    }
    int mid=(L+R)/2;
    if (k<=mid) m2(p*2,L,mid,k,x);
    else m2(p*2+1,mid+1,R,k,x);
    gcd2[p]=gcd(gcd2[p*2],gcd2[p*2+1]);
}
int q2(int p,int L,int R,int l,int r)
{
    if (l<=L&&R<=r) return gcd2[p];
    int ret=0,mid=(L+R)/2;
    if (l<=mid) ret=gcd(ret,q2(p*2,L,mid,l,r));
    if (r>=mid+1) ret=gcd(ret,q2(p*2+1,mid+1,R,l,r));
    return ret;
}
void pre()
{
    int i;
    dep[1]=1;
    dfs1(1);
    top[1]=1;
    dfs2(1);
    for (i=1;i<=n;i++)
      m1(1,1,tot,pos[i],pos[i],v[i]);
    for (i=1;i<=n;i++)
      if (son[i])
        m2(1,1,tot,pos[son[i]],v[i]-v[son[i]]);
}
int query(int u,int v)
{
    int f1,f2,ret=0;
    while ((f1=top[u])!=(f2=top[v]))
    {
        if (dep[f1]<dep[f2])
        {
            swap(u,v);
            swap(f1,f2);
        }
        if (f1!=u) ret=gcd(ret,q2(1,1,tot,pos[son[f1]],pos[u]));
        ret=gcd(ret,q1(1,1,tot,pos[f1]));
        u=fa[f1];
    }
    if (dep[u]<dep[v])
      swap(u,v);
    if (u!=v) ret=gcd(ret,q2(1,1,tot,pos[son[v]],pos[u]));
    ret=gcd(ret,q1(1,1,tot,pos[v]));
    return ret;
}
void modify(int u,int v,int x)
{
    int f1,f2;
    while ((f1=top[u])!=(f2=top[v]))
    {
        if (dep[f1]<dep[f2])
        {
            swap(u,v);
            swap(f1,f2);
        }
        m1(1,1,tot,pos[f1],pos[u],x);
        if (son[u]) m2(1,1,tot,pos[son[u]],x);
        u=fa[f1];
    }
    if (dep[u]<dep[v])
      swap(u,v);
    m1(1,1,tot,pos[v],pos[u],x);
    if (son[u]) m2(1,1,tot,pos[son[u]],x);
    if (son[fa[v]]==v) m2(1,1,tot,pos[v],-x);
}
int main()
{
    int x,y,z;
    char s[5];
    init();
    pre();
    scanf("%d",&m);
    while (m--)
    {
        scanf("%s",s);
        if (s[0]=='F')
        {
            scanf("%d%d",&x,&y);
            x++;
            y++;
            printf("%d\n",query(x,y));
        }
        else
        {
            scanf("%d%d%d",&x,&y,&z);
            x++;
            y++;
            modify(x,y,z);
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值