题意:
给一棵n个顶点的树,每个顶点有点权,树的根为1
假设u是v的祖先,定义f(u,v)为树上路径u到v的点权gcd
现在要计算对于所有祖先关系u和v,f(u,v)的和是多少,答案对1e9+7取模
ps:自己也是自己的祖先
数据范围:n<=1e5,点权<=1e12
解法:
一个结论:
长度为n的区间,必选a(n)的所有子区间[x,n]中不同gcd的数量不超过logn
证明:
倒过来比较好解释,把必选a(n)的所有子区间[x,n]改成必选a(1)的所有子区间[1,x]
可以看作从a(1)开始按照区间从左到右依次插入一个数,然后他们的gcd
由于这样的区间gcd是单调不增的,如果要使得插入一个数之后gcd与之前的不同,那么每次最少需要除去一个质因子,
要使得数量最多的话,最优情况下就是在之前gcd的基础上除以一个最小质因子2,那么最多logn个
开vector从上到下维护链的子区间所有gcd和该gcd的数量就行了
另外这题只能从根节点向下计算,而不能从叶子向上计算
因为叶子节点很多(会形成多条链),向上传递的时候不同的gcd会堆积在一起,数量太多了,会超时。
从上到下一定是一条链,因此能保证最多只有logn个
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
//
const int maxm=1e5+5;
const int mod=1e9+7;
typedef pair<int,int> PI;
vector<PI>fac[maxm];
vector<int>g[maxm];
int a[maxm];
int ans;
int n;
void dfs(int x,int fa){
map<int,int>mark;
mark[a[x]]++;
for(PI i:fac[fa]){
int num=i.first;
int cnt=i.second;
int t=gcd(num,a[x]);
mark[t]+=cnt;
}
for(auto i:mark){
fac[x].push_back({i.first,i.second});
ans=(ans+i.first*i.second%mod)%mod;
}
for(int v:g[x]){
if(v==fa)continue;
dfs(v,x);
}
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<n;i++){
int a,b;cin>>a>>b;
g[a].push_back(b);
g[b].push_back(a);
}
dfs(1,0);
cout<<ans<<endl;
return 0;
}