题意:

给你一棵图片说明 个节点的无根树,每个节点有一个权值,给你图片说明 次询问,每次询问你需要求出点图片说明 到点图片说明 之间的简单路径所经过的点的权值积,答案对图片说明 取模。

方法一(暴力求解)

对于每次询问,我们可以把点图片说明 和点图片说明 都跳到它们的最近公共祖先上,然后把经过的点的权值作为贡献乘到答案里。
接下来是如何跳到最近公共祖先上?
我们可以首先预处理出每个点的深度,每个点的父亲节点,显然只需要一次DFS即可。
然后我们可以把点图片说明 和点图片说明 暴力往上跳,直到跳到它们的最近公共祖先上。
具体的:每次选择深度较大的一个点往上跳,直到跳到跟另外一个点深度相同。接着两个点同时向上跳,直到两个点相遇,即找到最近公共祖先。把跳的过程中遇到的点的权值乘到答案里即可。
代码:

class Solution {
public:
    const int mod=1e9+7;
    struct graph{
        int head[100005],nxt[100005<<1],to[100005<<1],cnt;
        inline graph():cnt(1){}
        inline void add(int u,int v){
            nxt[++cnt]=head[u];
            to[cnt]=v;
            head[u]=cnt;
        }
        inline void clear(){
            cnt=1;
            memset(head,0,sizeof head);
            memset(nxt,0,sizeof nxt);
            memset(to,0,sizeof to);
        }
    }gr;//链式前向星存图
    int val[100005];//val[i] 表示点i的权值
    int dep[100005];//dep[i] 表示点i的深度
    int father[100005];//father[i] 表示点i的父亲节点
    void dfs(int u){//预处理出dep数组和father数组
        dep[u]=dep[father[u]]+1;//当前节点的深度=父亲节点的深度+1
        for(int i=gr.head[u];i;i=gr.nxt[i]){
            int v=gr.to[i];
            if(v==father[u])continue;
            father[v]=u;
            dfs(v);
        }
    }
    int query(int x,int y){
        if(dep[x]<dep[y])swap(x,y);//每次先选择深度较大的点向上跳
        int res=1;
        while(dep[x]>dep[y]){
            res=1ll*res*val[x]%mod;
            x=father[x];
            if(x==y){//如果x和y的最近公共祖先就是y
                res=1ll*res*val[x]%mod;
                return res;
            }
        }
        //此时x和y的深度一样,两个一起往上跳
        while(x!=y){
            res=1ll*res*val[x]%mod;
            res=1ll*res*val[y]%mod;
            x=father[x];
            y=father[y];
        }
        //此时x=y
        res=1ll*res*val[x]%mod;
        return res;
    }
    inline void clearAll(){
        gr.clear();
        memset(val,0,sizeof val);
        memset(dep,0,sizeof dep);
        memset(father,0,sizeof father);
    }
    vector<int> solve(int n, int m, vector<int>& a, vector<int>& u, vector<int>& v, vector<int>& x, vector<int>& y) {
        clearAll();
        for(int i=1;i<=n;i++){
            val[i]=a[i-1];
        }
        for(int i=1;i<n;i++){//n-1条边
            gr.add(u[i-1],v[i-1]);
            gr.add(v[i-1],u[i-1]);
        }
        dfs(1);
        vector<int> ans;
        for(int i=0;i<m;i++){
            ans.push_back(query(x[i],y[i]));
        }
        return ans;
    }
};

时间复杂度:对于每次询问,最坏情况是要跳n个点,所以时间复杂度是图片说明
空间复杂度:所有数组的规模都是图片说明 的,故空间复杂度为图片说明

方法二(正解)

前置知识:树上倍增

我们可以利用倍增法来优化方法一的跳的过程。
具体的:
我们令图片说明 表示点x向上跳图片说明 步遇到的点。
我们令图片说明 表示包括点x在内的向上图片说明 个点的权值积。
那么,如何求解上面两个数组呢?
显然有:
图片说明
图片说明
(跳图片说明步 相当于先跳图片说明 步,再跳图片说明 步)
于是我们可以DFS整棵树的过程中求解出上面的数组。
由于任意一个数字我们都可以进行二进制分解,故一个点跳任意步数我们都可以用上面的数组实现。
于是我们就把图片说明 的暴力跳优化到图片说明 了。
代码:

class Solution {
public:
    const int mod=1e9+7;
    struct graph{
        int head[100005],nxt[100005<<1],to[100005<<1],cnt;
        inline graph():cnt(1){}
        inline void add(int u,int v){
            nxt[++cnt]=head[u];
            to[cnt]=v;
            head[u]=cnt;
        }
        inline void clear(){
            memset(head,0,sizeof head);
            memset(nxt,0,sizeof nxt);
            memset(to,0,sizeof to);
            cnt=1;
        }
    }gr;//链式前向星存图
    int up[100005][18],tval[100005][18],dep[100005];
    //up,tval数组见题解,dep[i]表示点i的深度
    void dfs(int u,int fa){//首先dfs整棵树,预处理出上面的数组
        dep[u]=dep[fa]+1;
        for(int j=1;j<=17;j++){
            up[u][j]=up[up[u][j-1]][j-1];
            tval[u][j]=1ll*tval[u][j-1]*tval[up[u][j-1]][j-1]%mod;
        }
        for(int i=gr.head[u];i;i=gr.nxt[i]){
            int v=gr.to[i];
            if(v==fa)continue;
            up[v][0]=u;
            dfs(v,u);
        }
    }
    int query(int x,int y){
        if(dep[x]<dep[y])swap(x,y);
        int res=1;
        for(int j=17;j>=0;j--){//首先选择深度较大的点往上跳,
            if(dep[up[x][j]]>=dep[y]){
                res=1ll*res*tval[x][j]%mod;
                x=up[x][j];
            }
            if(x==y){//如果x和y的最近公共祖先就是y
                res=1ll*res*tval[x][0]%mod;
                return res;
            }
        }
        for(int j=17;j>=0;j--){
            if(up[x][j]!=up[y][j]){//然后两个点一起跳
                res=1ll*res*tval[x][j]%mod;
                res=1ll*res*tval[y][j]%mod;
                x=up[x][j];
                y=up[y][j];
            }
        }
        /*此处可画图理解*/
        res=1ll*res*tval[x][0]%mod;
        res=1ll*res*tval[y][0]%mod;
        res=1ll*res*tval[up[x][0]][0]%mod;
        return res;
    }
    inline void clearAll(){//多测要清空
        gr.clear();
        memset(up,0,sizeof up);
        memset(tval,0,sizeof tval);
        memset(dep,0,sizeof dep);
    }
    vector<int> solve(int n, int m, vector<int>& a, vector<int>& u, vector<int>& v, vector<int>& x, vector<int>& y) {
        for(int i=0;i<n;i++){
            tval[i+1][0]=a[i];
        }
        for(int i=1;i<n;i++){
            gr.add(u[i-1],v[i-1]);
            gr.add(v[i-1],u[i-1]);
        }
        dfs(1,0);
        vector<int> ans;
        for(int i=0;i<m;i++){
            ans.push_back(query(x[i],y[i]));
        }
        return ans;
    }
};

时间复杂度:图片说明 次询问,每次询问都是图片说明 的,故时间复杂度为图片说明
空间复杂度:我们需要预处理出倍增数组图片说明图片说明 ,都是图片说明 规模的,故空间复杂度为图片说明