White Falcon And Tree
Difficulty: Hard

White Falcon has a tree with $N$ nodes. Each node contains a linear function. Let's denote by $f_u(x)$ the linear function contained in the node $u$.  

Let's denote the path from node $u$ to node $v$ like this: $p_1,p_2,p_3,\ldots,p_k$, where $p_1 = u$ and $p_k = v$, and $p_i$ and $p_{i+1}$ are connected.  

White Falcon also has $Q$ queries. They are in the following format:  

1. $1$ $u$ $v$ $a$ $b$. Assign $ax + b$ as the function of all the nodes on the path from $u$ to $v$, i.e., $f_{p_i}(x)$ is changed to $ax+b$ where $p_1,p_2,p_3,\ldots,p_k$ is the path from $u$ to $v$.

2. $2$ $u$ $v$ $x$. Calculate $f_{p_k}(f_{p_{k-1}}(f_{p_{k-2}}(\ldots f_{p_1}(x))))$ modulo $(10^9 + 7)$


Input Format:
The first line contains $N$, the number of nodes. The following $N$ lines each contain two integers $a$ and $b$ that describe the function $ax + b$.  

Following $N - 1$ lines contain edges of the tree. 

The next line contains $Q$, the number of queries. Each subsequent line contains one of the queries described above.

Output Format:
For every query of the second kind, print one line containing an integer, the answer for that query.  

**Constraints**  
$1 \leq  N \leq  50000$ (Number of nodes)   
$1 \leq  Q \leq  50000$ (Number of queries)   
$0 \leq a, b, x < 10^9 + 7$


Sample Input:
    2
    1 1
    1 2
    1 2
    2
    1 2 2 1 1
    2 1 2 1

Sample Output:
3

Python 3 starter code:


Reference solution (C++):
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <complex>
    #include <cassert>
    #include <cstdlib>
    #include <cstdio>
    #include <bitset>
    #include <vector>
    #include <string>
    #include <cmath>
    #include <ctime>
    #include <queue>
    #include <list>
    #include <map>
    #include <set>

    #define all(x) (x).begin(), (x).end()
    #define type(x) __typeof((x).begin())
    #define foreach(it, x) for(type(x) it = (x).begin(); it != (x).end(); it++)

    //!! debug purposes
    #ifdef KAZAR
        #define eprintf(...) fprintf(stderr,__VA_ARGS__)
    #else
        #define eprintf(...) 0
    #endif

    using namespace std;

    template<class T> inline void umax(T &a,T b){if(a<b) a = b ; }
    template<class T> inline void umin(T &a,T b){if(a>b) a = b ; }
    template<class T> inline T abs(T a){return a>0 ? a : -a;}
    template<class T> inline T gcd(T a,T b){return __gcd(a, b);}
    template<class T> inline T lcm(T a,T b){return a/gcd(a,b)*b;}

    const int inf = 1e9 + 143;
    const long long longinf = 1e18 + 143;

    inline int read(){int x;scanf(" %d",&x);return x;}

    const bool CHECK_SOLUTION = true;

    const int N = 1 << 18;

    const int mod = 1e9 + 7;

    inline int add(int x,int y){return (x + y >= mod)? x + y - mod : x + y;}
    inline int mul(int x,int y){return (long long)x * y % mod;}

    inline int power(int x,int n){
        int res = 1;
        while(n > 0){
            if(n & 1)
                res = mul(res, x);
            n >>= 1;
            x = mul(x, x);
        }
        return res;
    }

    struct func{
        int a, b;
        func() : a(-1), b(-1) {};
        func(int _a,int _b) : a(_a), b(_b) {}
        void debug(){
            eprintf("%d %d\n",a,b);
        }
    };

    func merge(const func &g,const func &f){
        if(f.a == -1) return g;
        if(g.a == -1) return f;
        return func(mul(f.a, g.a), add(mul(f.a, g.b), f.b));
    }

    inline int sums(int x,int n){
        int p = power(x, n) - 1;
        if(p < 0) p += mod;
        return mul(p, power(x - 1, mod - 2));
    }

    func calc(const func &f,int n){
        int s = (f.a == 0)? 1 : ((f.a == 1)? n : sums(f.a, n));
        return func(power(f.a, n), mul(s, f.b));
    }

    int tree_sz[N << 1];

    namespace treeLR{

        func tree[N << 1], lazy[N << 1];

        inline void push(int x){
            if(lazy[x].a != -1){
                lazy[x + x] = lazy[x];
                lazy[x + x + 1] = lazy[x];
                lazy[x].a = -1;
            }
        }

        func applied(int x){
            if(lazy[x].a != -1)
                return calc(lazy[x], tree_sz[x]);
            return tree[x];
        }

        inline void relax(int x){
            tree[x] = merge(applied(x + x), applied(x + x + 1));// order matters
        }

        void upd(int x,int l,int r,int x1,int x2,func by){
            if(l > x2 || r < x1) return;
            if(l >= x1 && r <= x2){
                lazy[x] = by;
                return;
            }
            push(x);
            int m = (l + r) >> 1;
            upd(x + x, l, m, x1, x2, by);
            upd(x + x + 1, m + 1, r, x1, x2, by);
            relax(x);
        }

        func get(int x,int l,int r,int x1,int x2){
            if(l > x2 || r < x1) return func(-1,-1);
            if(l >= x1 && r <= x2) return applied(x);
            push(x);
            int m = (l + r) >> 1;
            func L = get(x + x, l, m, x1, x2);
            func R = get(x + x + 1, m + 1, r, x1, x2);
            relax(x);
            return merge(L, R); // order matters
        }

    };

    namespace treeRL{

        func tree[N << 1], lazy[N << 1];

        inline void push(int x){
            if(lazy[x].a != -1){
                lazy[x + x] = lazy[x];
                lazy[x + x + 1] = lazy[x];
                lazy[x].a = -1;
            }
        }

        func applied(int x){
            if(lazy[x].a != -1)
                return calc(lazy[x], tree_sz[x]);
            return tree[x];
        }

        inline void relax(int x){
            tree[x] = merge(applied(x + x + 1), applied(x + x));
        }

        void upd(int x,int l,int r,int x1,int x2,func by){
            if(l > x2 || r < x1) return;
            if(l >= x1 && r <= x2){
                lazy[x] = by;
                return;
            }
            push(x);
            int m = (l + r) >> 1;
            upd(x + x, l, m, x1, x2, by);
            upd(x + x + 1, m + 1, r, x1, x2, by);
            relax(x);
        }

        func get(int x,int l,int r,int x1,int x2){
            if(l > x2 || r < x1) return func(-1,-1);
            if(l >= x1 && r <= x2) return applied(x);
            push(x);
            int m = (l + r) >> 1;
            func L = get(x + x, l, m, x1, x2);
            func R = get(x + x + 1, m + 1, r, x1, x2);
            relax(x);
            return merge(R, L);//order matters again
        }

    };

    int fa[N], fb[N];
    vector<int> g[N];
    int sz[N], d[N], par[N];
    int id[N], ptr = 0;
    int inv_id[N];
    int up[N]; // for heavy chain

    void dfs1(int u,int p){
        sz[u] = 1;
        foreach(it, g[u]){
            int v = *it;
            if(v != p){
                d[v] = d[u] + 1;
                par[v] = u;
                dfs1(v, u);
                sz[u] += sz[v];
            }
        }
    }

    void dfs2(int u,int p){
        id[u] = ++ptr;
        inv_id[ptr] = u;
        int bigid = -1;
        foreach(it, g[u]){
            int v = *it;
            if(v != p && (bigid == -1 || sz[bigid] < sz[v]))
                bigid = v;
        }
        if(bigid != -1){
            up[bigid] = up[u]? up[u] : u;
            dfs2(bigid, u);
        }
        foreach(it, g[u]){
            int v = *it;
            if(v != bigid && v != p){
                up[v] = 0;
                dfs2(v, u);
            }
        }
    }

    int get_lca(int u,int v){// calculate lca with H-L decs.
        for(;;){
            int nu = up[u]? up[u] : u;
            int nv = up[v]? up[v] : v;
            if(nu == nv){
                return (d[u] < d[v])? u : v;
            }else{
                if(d[nu] > d[nv]){
                    u = par[nu];
                }else{
                    v = par[nv];
                }
            }
        }
    }

    void upd_path(int u,int p,func f){// update all the nodes for both LR and RL
        eprintf("Updating %d %d with %d %d\n",u,p,f.a,f.b);
        while(d[u] > d[p]){
            if(!up[u]){
                treeLR::upd(1, 1, N, id[u], id[u], f);
                treeRL::upd(1, 1, N, id[u], id[u], f);
                eprintf("upd : %d\n",u);
                u = par[u];
            }else{
                int next = up[u];
                if(d[next] < d[p]){
                    next = p;
                }
                eprintf("upd : (%d to %d]\n",next,u);
                treeLR::upd(1, 1, N, id[next] + 1, id[u], f);
                treeRL::upd(1, 1, N, id[next] + 1, id[u], f);
                u = next;
            }
        }
        eprintf("upd : %d\n",p);
        treeLR::upd(1, 1, N, id[p], id[p], f);
        treeRL::upd(1, 1, N, id[p], id[p], f);
        eprintf("End of update\n");
    }

    func get_left(int u,int p){ // including p, travelling from u to p., remember order matters!!!!
        eprintf("Start get_left %d %d\n",u,p);
        func cur;
        while(d[u] > d[p]){
            if(!up[u]){
                eprintf("append [%d, %d]\n", id[u], id[u]);
                treeRL::get(1, 1, N, id[u], id[u]).debug();
                cur = merge(cur, treeRL::get(1, 1, N, id[u], id[u]));
                u = par[u];
            }else{
                int next = up[u];
                if(d[next] < d[p]){
                    next = p;
                }
                eprintf("append [%d, %d]\n", id[next] + 1, id[u]);
                treeRL::get(1, 1, N, id[next] + 1, id[u]).debug();
                cur = merge(cur, treeRL::get(1, 1, N, id[next] + 1, id[u]));
                u = next;
            }
        }
        eprintf("cur\n");
        cur.debug();
        eprintf("append [%d, %d]\n",id[p],id[p]);
        treeLR::get(1, 1, N, id[p], id[p]).debug();
        cur = merge(cur, treeRL::get(1, 1, N, id[p], id[p]));
        eprintf("final func : %d %d\n",cur.a,cur.b);
        eprintf("End of get_left\n");
        return cur;
    }

    func get_right(int u,int p,bool add_damn_parent){ // p is not including, travelling from p to u
        eprintf("Introduce bloddy get_right\n");
        func cur;
        while(d[u] > d[p]){
            if(!up[u]){
                cur = merge(treeLR::get(1, 1, N, id[u], id[u]), cur);
                u = par[u];
            }else{
                int next = up[u];
                if(d[next] < d[p]){
                    next = p;
                }
                eprintf("fuck fuck fuck append this : [%d,%d]\n",id[next] + 1,id[u]);
                treeRL::get(1, 1, N, id[next] + 1, id[u]).debug();
                cur = merge(treeLR::get(1, 1, N, id[next] + 1, id[u]), cur);
                u = next;
            }
        }
        if(add_damn_parent){
            eprintf("damn parent coming : [%d,%d]\n",id[p],id[p]);
            cur = merge(treeLR::get(1, 1, N, id[p], id[p]), cur);
        }
        eprintf("The End bloddy get_right\n");
        return cur;
    }

    func get_path(int u,int v){
        int lca = get_lca(u, v);
        if(lca == u){
            return get_right(v, u, true);
        }
        if(lca == v){
            eprintf("left\n");
            get_left(u, v).debug();
            return get_left(u, v);
        }
        eprintf("-----------left\n");
        get_left(u, lca).debug();
        eprintf("----------right\n");
        get_right(v, lca, false).debug();
        return merge(get_left(u, lca), get_right(v, lca, false));
    }

    namespace naive{
        void upd_path(int u,int p,int a,int b){
            for(;;){
                fa[u] = a;
                fb[u] = b;
                if(u == p)
                    break;
                u = par[u];
            }
        }
        int calc(int u,int x,int dest,int p){
            if(u == dest) return x;
            foreach(it, g[u]){
                int v = *it;
                if(v == p) continue;
                int t = calc(v, add(mul(fa[v], x), fb[v]), dest, u);
                if(t != -1)
                    return t;
            }
            return -1;
        }
        int get_path(int u,int v,int x){
            return calc(u, add(mul(fa[u], x), fb[u]), v, 0);
        }
    };

    int main(){

    #ifdef KAZAR
        freopen("f.input","r",stdin);
        freopen("f.output","w",stdout);
        freopen("error","w",stderr);
    #endif

        for(int i = N; i < N + N; i++)
            tree_sz[i] = 1;
        for(int i = N - 1; i > 0; i--)
            tree_sz[i] = tree_sz[i + i] + tree_sz[i + i + 1];

        int n = read();

        for(int i = 1; i <= n; i++){
            fa[i] = read();
            fb[i] = read();
            assert(fa[i] >= 0 && fa[i] < mod);
            assert(fb[i] >= 0 && fb[i] < mod);
        }

        for(int i = 1; i < n; i++){
            int u = read();
            int v = read();
            g[u].push_back(v);
            g[v].push_back(u);
        }

        dfs1(1, 0);
        dfs2(1, 0);

        for(int i = 1; i <= n; i++){
            eprintf("id[%d] = %d\n",i,id[i]);
            treeLR::upd(1, 1, N, id[i], id[i], func(fa[i], fb[i]));
            treeRL::upd(1, 1, N, id[i], id[i], func(fa[i], fb[i]));
        }

        int q = read();
        for(int i = 0; i < q; i++){
            int t = read();
            if(t == 1){
                int u = read(), v = read();
                int a = read(), b = read();
                int lca = get_lca(u, v);
                eprintf("lca : %d\n",lca);
                upd_path(u, lca, func(a, b));
                upd_path(v, lca, func(a, b));
                if(CHECK_SOLUTION){
                    naive::upd_path(u, lca, a, b);
                    naive::upd_path(v, lca, a, b);
                }
            }else{
                int u = read(), v = read(), x = read();
                func f = get_path(u, v);
                printf("%d\n",add(mul(f.a, x), f.b));
                if(CHECK_SOLUTION){
                    if(naive::get_path(u, v, x) != add(mul(f.a, x), f.b)){
                        eprintf("Lca : %d\n",get_lca(u, v));
                        eprintf("on the %d -> %d with %d\n",u,v,x);
                        eprintf("naive : %d\n",naive::get_path(u, v, x));
                        eprintf("fastt : %d\n",add(mul(f.a, x), f.b));
                        exit(123);
                    }
                }
            }
        }

        return 0;
    }