5 3 2 3 5 2500 200 9 270000 27 4 2 3 5 2 5 4 1
1
给出一棵树形图,每个顶点都有权值,该权值的所有因子都是给出的k个素数里面的数字,即每个权值都是有给出的素数相乘得到,问存在多少的旅游路线,使所有的权值相乘后是一个立方数(如1,8,27……)因为给出的数是int64范围内的,直接相加比较会超出int64的范围,因此要巧妙的运用给出的素因子的性质,要是总的积是一个立方数,则该立方数的素因子个数一定是3的倍数,因此可以把指数幂转化成三进制的数进行储存,例如2500的因子有2个2,0个3和4个5,对每个数字对3取余之后为产生的三进制表示法是201,对应的十进制是2*3^2+0+1*3^0=19;所以相乘的问题就转换成了三进制数相加的问题了,之后就是树的分治问题。
首先对于一颗子树,求出所有孩子与该根节点(包含根节点)的链的三进制数的和,如果该三进制数是0,则满足条件,然后对于不是0的三进制数,搜索看看是不是存在对立面(对立面为0-该不是0的三进制数),若存在也是一种跨立根节点的满足条件的链
具体过程:对于一个根节点,加入有k个儿子,然后对于每个儿子进行深搜,找到所有经过该儿子和根节点的链,统计满足条件的个数,之后顺便记录该儿子的子树所有节点不包含根节点的链,用num[a]=b记录,即三进制数a的个数有b个,这些数值作为搜下一个儿子子树时的对立面,等该根节点所有儿子的子树搜索完毕之后把num数组清空,重复以上子树遍历过程,最后把每个子树的统计结果相加就是答案:
最后别忘了统计一下某个点自身的权值是不是立方数。
程序:
#pragma comment(linker, "/STACK:1024000000,1024000000") #include"stdio.h" #include"string.h" #include"iostream" #include"map" #include"string" #include"queue" #include"stdlib.h" #include"math.h" #define M 50009 #define eps 1e-10 #define inf 99999999 #define mod 1000000000 using namespace std; struct node { int u,v,next; }edge[M*3]; int t,head[M],use[M],son[M],limit[M],k,MN,ID,cnt,ans; __int64 prime[33],value[M],cube[33],dis[M]; void init() { t=0; memset(head,-1,sizeof(head)); } void add(int u,int v) { edge[t].u=u; edge[t].v=v; edge[t].next=head[u]; head[u]=t++; } __int64 Add(__int64 m,__int64 n) { int a[33],b[33],i,j; for(i=0;i<k;i++) a[i]=b[i]=0; i=0; while(m) { a[i++]=m%3; m/=3; } j=0; while(n) { b[j++]=n%3; n/=3; } __int64 ss=0; for(i=0;i<k;i++) ss+=cube[i]*((a[i]+b[i])%3); return ss; }//三进制数相加 __int64 down(__int64 m,__int64 n) { int a[33],b[33],i,j; for(i=0;i<k;i++) a[i]=b[i]=0; i=0; while(m) { a[i++]=m%3; m/=3; } j=0; while(n) { b[j++]=n%3; n/=3; } __int64 ss=0; for(i=0;i<k;i++) ss+=cube[i]*((3+a[i]-b[i])%3); return ss; }//三进制数相减 void dfs_size(int u,int f) { son[u]=1; limit[u]=0; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; if(f!=v&&!use[v]) { dfs_size(v,u); son[u]+=son[v]; limit[u]=max(limit[u],son[v]); } } } void dfs_root(int root,int u,int f) { if(son[root]-son[u]>limit[u]) limit[u]=son[root]-son[u]; if(MN>limit[u]) { MN=limit[u]; ID=u; } for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; if(f!=v&&!use[v]) { dfs_root(root,v,u); } } }//以上两个递归求树的重心 void dfs_dis(int u,int f,__int64 id) { dis[cnt++]=id; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; if(v!=f&&!use[v]) { dfs_dis(v,u,Add(id,value[v])); } } }//求子树的权值和 __int64 cal(int u,int f)//对于每个根节点统计满足条件的情况 { int sum=0; map<__int64,int>mp; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; if(v!=f&&!use[v]) { cnt=0; dfs_dis(v,u,Add(value[ID],value[v])); for(int j=0;j<cnt;j++) { if(dis[j]==0) sum++; __int64 op=down(0,dis[j]); sum+=mp[op]; } for(int j=0;j<cnt;j++) { mp[down(dis[j],value[ID])]++; } } } return sum; } void dfs_ans(int root,int u,int f)//递归分解子树 { dfs_size(u,f); MN=inf; dfs_root(root,u,f); ans+=cal(ID,ID); use[ID]=1; for(int i=head[ID];i!=-1;i=edge[i].next) { int v=edge[i].v; if(!use[v]) { dfs_ans(v,v,v); } } } void slove(int n) { memset(use,0,sizeof(use)); ans=0; dfs_ans(1,1,1); for(int i=1;i<=n;i++) if(value[i]==0) ans++; printf("%d\n",ans); } int main() { int n,i,j; cube[0]=1; for(i=1;i<30;i++) cube[i]=cube[i-1]*3; while(scanf("%d",&n)!=-1) { scanf("%d",&k); for(i=0;i<k;i++) scanf("%I64d",&prime[i]); for(i=1;i<=n;i++) { value[i]=0; __int64 p; scanf("%I64d",&p); for(j=0;j<k;j++) { int r=0; while(p%prime[j]==0) { p=p/prime[j]; r++; r%=3; } value[i]+=cube[k-j-1]*(r%3); } } init(); for(i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v); add(v,u); } slove(n); } return 0; }