zoukankan      html  css  js  c++  java
  • CodeChef DGCD

    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 gcdof 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 Ndenoting 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.

    Example

    Input:
    6
    0 4
    0 5
    1 5
    5 2
    3 5
    3 1 3 2 4 2
    5
    F 3 5
    C 1 3 1
    C 3 4 4
    F 3 0
    F 2 5
    
    Output:
    2
    7
    1
    大致题意:给一棵树,每个点都有点权.现在有两种操作:1.将u到v的路径上的点的点权都加k. 2.求u到v的路径上的点的gcd.
    分析:两个点之间的路径,很容易想到树链剖分.常见的套路用线段树维护,但是这样的话就不好处理区间加了.需要用到gcd的一个性质:
    gcd(a,b,c,d......) = gcd(a,b-c,c-d,......).也就是说只需要知道首元素的值和差分后序列的gcd就可以了.因为差分过了,所以修改操作可以直接变成单点修改.在pushup的时候就能更新gcd.这样的话会有一个问题.差分修改一个区间的后继,在树上,一个点可能有多个子节点,那么它的后继是谁呢?很容易就能想到是重儿子,因为我们查询和修改每次都是从重链往上跳,用到的自然就是重儿子的值.那么整体的思路就是用线段树维护差分+区间修改+单点查询.
    说起来挺容易的,其实写起来比较复杂.首先差分有可能将数变成负的,gcd求出来就可能是一个负数,解决方法就是在最后输出答案的时候加一个abs. 第二个就是这道题建树的时候因为是要建差分序列,所以要在第二次dfs的时候处理出每个id对应的值(见代码),再一个就是查询的时候可能会遇到空区间,要及时特判掉. 最坑的就是线段树的标记下传会T掉!这道题卡时卡的丧心病狂,正确的做法是将一路的标记直接加上,不下传.最好使用快读.
    调了一天QAQ,不过最后终于卡着时过了.
    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 50010;
    
    int n,q,head[maxn],to[maxn * 2],nextt[maxn * 2],tot = 1,v[maxn];
    int dep[maxn],sizee[maxn],top[maxn],cnt,son[maxn],id[maxn],idx[maxn],fa[maxn];
    int c[maxn << 2],tag[maxn << 2],g[maxn << 2],val[maxn];
    
    inline int read()
    {
        int ans,f=1;char ch;
        while ((ch=getchar())<'0'||ch>'9') if (ch=='-') f=-1;ans=ch-'0';
        while ((ch=getchar())>='0'&&ch<='9') ans=ans*10+ch-'0';
        return ans*f;
    }
    
    int gcd(int a,int b)
    {
        if (!b)
            return a;
        return gcd(b,a % b);
    }
    
    void add(int x,int y)
    {
        to[tot] = y;
        nextt[tot] = head[x];
        head[x] = tot++;
    }
    
    void dfs(int u,int d,int from)
    {
        dep[u] = d;
        sizee[u] = 1;
        fa[u] = from;
        for (int i = head[u];i;i = nextt[i])
        {
            int v = to[i];
            if (v == from)
                continue;
            dfs(v,d + 1,u);
            sizee[u] += sizee[v];
            if (sizee[v] >= sizee[son[u]])
                son[u] = v;
        }
    }
    
    void dfs2(int u,int topp)
    {
        top[u] = topp;
        id[u] = ++cnt;
        val[cnt] = v[u];
        if (son[u])
            dfs2(son[u],topp);
        for (int i = head[u];i;i = nextt[i])
        {
            int v = to[i];
            if (v == son[u] || v == fa[u])
                continue;
            dfs2(v,v);
        }
    }
    
    void pushup(int o)
    {
        g[o] = gcd(g[o * 2],g[o * 2 + 1]);
    }
    
    void build(int o,int l,int r)
    {
        if (l == r)
        {
            c[o] = val[l];
            g[o] = val[l] - val[l - 1];
            return;
        }
        int mid = (l + r) >>1;
        build(o * 2,l,mid);
        build(o * 2 + 1,mid + 1,r);
        pushup(o);
    }
    
    void update1(int o,int l,int r,int x,int y,int v) //权值区间修改
    {
        if (x <= l && r <= y)
        {
            c[o] += v;
            return;
        }
        int mid = (l + r) >> 1;
        if (x <= mid)
            update1(o * 2,l,mid,x,y,v);
        if (y > mid)
            update1(o * 2 + 1,mid + 1,r,x,y,v);
    }
    
    void update2(int o,int l,int r,int cur,int v) //gcd单点修改
    {
        if (l == r)
        {
            g[o] += v;
            return;
        }
        pushdown(o);
        int mid = (l + r) >> 1;
        if (cur <= mid)
            update2(o * 2,l,mid,cur,v);
        if (cur > mid)
            update2(o * 2 + 1,mid + 1,r,cur,v);
        pushup(o);
    }
    
    int query1(int o,int l,int r,int cur) //单点询问权值
    {
        if (l == r)
            return c[o];
        pushdown(o);
        int mid = (l + r) >> 1;
        if (cur <= mid)
            return c[o] + query1(o * 2,l,mid,cur);
        if (cur > mid)
            return c[o] + query1(o * 2 + 1,mid + 1,r,cur);
    }
    
    int query2(int o,int l,int r,int x,int y)  //询问区间gcd
    {
        if (x > y)
            return 0;
        if (x <= l && r <= y)
            return g[o];
        pushdown(o);
        int mid = (l + r) >> 1,res = 0;
        if (x <= mid)
        {
            int t = query2(o * 2,l,mid,x,y);
            if (!res)
                res = t;
        }
        if (y > mid)
        {
            int t = query2(o *2 + 1,mid + 1,r,x,y);
            if (!res)
                res = t;
            else
                res = gcd(res,t);
        }
        return res;
    
    }
    
    int solve1(int x,int y)
    {
        int ans = 0;
        while (top[x] != top[y])
        {
            if (dep[top[x]] < dep[top[y]])
                swap(x,y);
            ans = gcd(ans,query1(1,1,n,id[top[x]]));
            ans = gcd(ans,query2(1,1,n,id[top[x]] + 1,id[x]));
            x = fa[top[x]];
        }
        if (dep[x] > dep[y])
            swap(x,y);
        ans = gcd(ans,query1(1,1,n,id[x]));
        ans = gcd(ans,query2(1,1,n,id[x] + 1,id[y]));
        return abs(ans);
    }
    
    void solve2(int x,int y,int d)
    {
        while (top[x] != top[y])
        {
            if (dep[top[x]] < dep[top[y]])
                swap(x,y);
            update1(1,1,n,id[top[x]],id[x],d);
            update2(1,1,n,id[top[x]],d);
            update2(1,1,n,id[x] + 1,-d);
            x = fa[top[x]];
        }
        if (dep[x] > dep[y])
            swap(x,y);
        update1(1,1,n,id[x],id[y],d);
        update2(1,1,n,id[x],d);
        update2(1,1,n,id[y] + 1,-d);
    }
    
    int main()
    {
        n = read();
        for (int i = 1; i < n; i++)
        {
            int u,v;
            u = read(),v = read();
            u++;
            v++;
            add(u,v);
            add(v,u);
        }
        for (int i = 1; i <= n; i++)
            v[i] = read();
        dfs(1,1,0);
        dfs2(1,1);
        build(1,1,n);
        q = read();
        while (q--)
        {
            char s[2];
            int a,b,c;
            scanf("%s",s);
            if (s[0] == 'F')
            {
                a = read();
                b = read();
                a++;
                b++;
                printf("%d
    ",solve1(a,b));
            }
            else
            {
                a = read();
                b = read();
                c = read();
                a++;
                b++;
                solve2(a,b,c);
            }
        }
    
        return 0;
    }
     
  • 相关阅读:
    s4-9 二层设备
    s4-9 二层设备
    s5-1 网络层引言
    LeetCode Factorial Trailing Zeroes (阶乘后缀零)
    UVA 658 It's not a Bug, it's a Feature! (最短路,经典)
    UVA 1151 Buy or Build (MST最小生成树,kruscal,变形)
    LeetCode Reverse Linked List (反置链表)
    LeetCode Contains Duplicate (判断重复元素)
    UVA 1395 Slim Span (最小生成树,MST,kruscal)
    割点,桥,边双连通分量,点双连通分量
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8094883.html
Copyright © 2011-2022 走看看