埃森哲杯第十六屆上海大學程式設計聯賽春季賽暨上海高校金馬五校賽B合約數
阿新 • • 發佈:2019-01-27
題目描述
在埃森哲,員工培訓是最看重的內容,最近一年,我們投入了 9.41 億美元用於員工培訓和職業發展。截至 2018 財年末,我們會在全球範圍內設立 100 所互聯課堂,將互動科技與創新內容有機結合起來。按崗培訓,按需定製,隨時隨地,本土化,區域化,虛擬化的培訓會讓你快速取得成長。小埃希望能通過培訓學習更多ACM 相關的知識,他在培訓中碰到了這樣一個問題,
給定一棵n個節點的樹,並且根節點的編號為p,第i個節點有屬性值vali, 定義F(i): 在以i為根的子樹中,屬性值是vali的合約數的節點個數。y 是 x 的合約數是指 y 是合數且 y 是 x 的約數。小埃想知道∑i*F(i) ,(i=0~n)對1000000007取模後的結果.
輸入描述:
輸入測試組數T,每組資料,輸入n+1行整數,第一行為n和p,1<=n<=20000, 1<=p<=n, 接下來n-1行,每行兩個整數u和v,表示u和v之間有一條邊。第n+1行輸入n個整數val1, val2,…, valn,其中1<=vali<=10000,1<=i<=n.
輸出描述:
對於每組資料,輸出一行,包含1個整數, 表示對1000000007取模後的結果
示例1
輸入
2
5 4
5 3
2 5
4 2
1 3
10 4 3 10 5
3 3
1 3
2 1
1 10 1
輸出
11
2
備註:
n>=10000的有20組測試資料
思路:對[1,10000]每個數字的合約數作預處理,然後用dfs序求解
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<iostream>
#include<string.h>
#include<vector>
#include<set>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
const int maxn = 2e4+10;
vector<int>g[maxn];
vector<int>vt[maxn];
bool vis[maxn];
int w[maxn];
int n,p;
ll cnt[maxn];
ll ans;
void init()//預處理[1,10000]每個數的合約數
{
//vis[]=1質數,vis[]=0合數
for(int i=1;i<=10000;i++) vis[i]=1;
for(int i=2;i<=10000;i++)
{
if(!vis[i]) continue;
for(int j=i+i;j<=10000;j+=i)
vis[j]=0;
}
for(int i=2;i<=10000;i++)
if(!vis[i])
{
for(int j=i;j<=10000;j+=i)
vt[j].push_back(i);
}
}
//i*f[i]相當於f[i]個i累加,即對於結點u來說,它的子節點中每個合約數結點都加上一個u
//整個樹中,一個結點i的cnt[i]等於結點x,y,z……的和(i是x,y,z的合約數)
void dfs(int u,int fa)
{
//將這個點的合約數全部加上這個點,這個點的合約數可能存在於子結點也可能是兄弟結點
for(int i=0;i<vt[w[u]].size();i++)
{
int v=vt[w[u]][i];
cnt[v]=(cnt[v]+u)%mod;
}
//搜到這個點時,這個點已經加過所有滿足條件的父結點
ans=(ans+cnt[w[u]])%mod;
for(int i=0;i<g[u].size();i++)
{
if(g[u][i]!=fa)
dfs(g[u][i],u);
}
//防止搜尋兄弟結點時加上這個點
for(int i=0;i<vt[w[u]].size();i++)
{
int v=vt[w[u]][i];
cnt[v]=(cnt[v]-u)%mod;
}
}
int main()
{
int T;
scanf("%d",&T);
init();
while(T--)
{
ans=0;
int n,p;
scanf("%d%d",&n,&p);
for(int i=1;i<=n;i++) g[i].clear();
for(int i=1;i<=n;i++) cnt[i]=0;
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
dfs(p,-1);
printf("%lld\n",ans);
}
return 0;
}