codeforces 986E-Prince's Problem

题意

给一棵有权值的树,每次询问给u,v,x,问u到v的路径上所有点的权值与x的gcd的乘积(对1e9+7取模)

分析

  • 离线搞
  • 对于每个素数单独处理
  • 每确定一个素数之后,从低到高枚举该素数的指数,并把跟这个素数相关的点拿出来更新在树上更新指数的大小。更新完毕之后,可以求得u,v之间指数的和(树状数组+倍增可以实现),再把与这个素数相关的所有询问取出来,更新询问。

给这个题跪了

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=100050;
const int N=1e7+10;
const ll MOD=1e9+7;
int prime[N],pz,isprime[N];
void get_prime(){
    memset(isprime,true,sizeof isprime);
    isprime[0]=isprime[1]=false;
    for(int i = 2; i < N; ++i){
        if(isprime[i]) prime[pz++]=i;
        for(int j = 0; (ll)i*prime[j]<N&& j<pz;++j){
            isprime[i*prime[j]]=false;
            if(i%prime[j]==0) break;
        }
    }
}

vector<int> G[maxn];
int n,q,f[maxn][20],L[maxn],R[maxn],dep[maxn],tot;
void dfs(int x,int par=0){
    L[x]=++tot;
    dep[x]=dep[par]+1;
    f[x][0]=par;
    for(int i = 1; f[x][i-1]; ++i) f[x][i]=f[f[x][i-1]][i-1];
    for(auto y:G[x])
        if(y!=par) dfs(y,x);
    R[x]=tot;
}
vector<pair<int,int> > v1[N],v2[N];
int X[maxn],Y[maxn],Z[maxn];
int sum[maxn];
void add(int x,int val){
    while(x<=n) sum[x]+=val,x+=(x&-x);
}
int Query(int x){
    int ans=0;
    while(x) ans+=sum[x],x-=(x&-x);
    return ans;
}
int lca(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    int h=dep[x]-dep[y];
    for(int i = 0; h ; ++i){
        if(h&(1<<i)){
            x=f[x][i];
            h^=(1<<i);
        }
    }
    if(x==y) return x;
    for(int i = 19; i >= 0; --i){
        if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    }
    return f[x][0];
}
ll ans[maxn];
ll Pow(ll x,ll n){
    ll ans=1,base=x%MOD;
    while(n){
        if(n&1) ans=ans*base%MOD;
        base=base*base%MOD;
        n>>=1;
    }
    return ans;
}
int dt[maxn];
int main(){
    get_prime();
    scanf("%d", &n);
    for(int i = 1; i < n; ++i){
        int x,y;
        scanf("%d%d", &x,&y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    dfs(1);
    for(int i = 1; i <= n; ++i){
        int x;
        scanf("%d", &x);
        for(int j = 0; (ll)prime[j]*prime[j]<=x; ++j){
            if(x%prime[j]==0){
                int cnt=0;
                while(x%prime[j]==0) x/=prime[j],cnt++;
                v1[prime[j]].push_back({cnt,i});
            }
        }
        if(x>1) v1[x].push_back({1,i});
    }
    int q;
    scanf("%d", &q);
    for(int i = 1; i <= q; ++i){
        int x;
        scanf("%d%d%d", &X[i],&Y[i],&x);
        Z[i]=lca(X[i],Y[i]);
        for(int j = 0; (ll)prime[j]*prime[j]<=x; ++j){
            if(x%prime[j]==0){
                int cnt=0;
                while(x%prime[j]==0) x/=prime[j],cnt++;
                v2[prime[j]].push_back({cnt,i});
            }
        }
        if(x>1) v2[x].push_back({1,i});
        ans[i]=1;
    }
    for(int i = 0; i < pz; ++i){
        int p=prime[i];
        if(v1[p].empty()||v2[p].empty()) continue;
        sort(v1[p].begin(),v1[p].end());
        sort(v2[p].begin(),v2[p].end());
        vector<int> v(v1[p].size(),0);
        int cur=0,cur2=0,m=v2[p][v2[p].size()-1].first;
        for(int j = 1; j <= m; ++j){
            while(cur<v1[p].size()&&v1[p][cur].first<j) cur++;
            for(int k = cur; k < v1[p].size(); ++k){
                int x=v1[p][k].second;
                add(L[x],1);
                add(R[x]+1,-1);
                v[k]++;
            }
            while(cur2<v2[p].size()&&v2[p][cur2].first<j) cur2++;
            
            while(cur2<v2[p].size()&&v2[p][cur2].first==j){
            
                int idx=v2[p][cur2].second;
                int x=X[idx],y=Y[idx],z=Z[idx],w=f[z][0];
                int d=Query(L[x])+Query(L[y])-Query(L[z])-Query(L[w]);
                ans[idx]=ans[idx]*Pow(p,d)%MOD;
                cur2++;
            }
        }
        for(int j = 0; j < v.size(); ++j){
            int x=v1[p][j].second;
            add(L[x],-v[j]);
            add(R[x]+1,v[j]);
        }
    }
    for(int i = 1; i <= q; ++i){
        printf("%lld\n", (ans[i]%MOD+MOD)%MOD);
    }
    return 0;
}

转载于:https://www.cnblogs.com/sciorz/p/9198022.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值