树链剖分-点的分治(链的点的个数为k的点对数)

hdu4760

Cube number on a tree

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 878    Accepted Submission(s): 162


Problem Description
The country Tom living in is famous for traveling. Every year, many tourists from all over the world have interests in traveling there.
There are n provinces in the country. According to the experiences from the tourists came before, every province has its own preference value. A route’s preference value from one province to another is defined as the product of all the preference value of the provinces on the route. It’s guaranteed that for each two provinces in the country there is a unique route from one to another without passing any province twice or more.
Tom is a boy crazy about cube number. A cube number is a positive integer whose cube root is also an integer. He is planning to travel from a province to another in the summer vacation and he will only choose the route with the cube number preference value. Now he want to know the number of routes that satisfy his strange requirement.
 

Input
The input contains several test cases, terminated by EOF.
Each case begins with a number n ( 1 ≤ n ≤ 50000), the number of the provinces.
The second line begins with a number K (1 ≤ K ≤ 30), and K difference prime numbers follow. It’s guaranteed that all the preference number can be represented by the product of some of this K numbers(a number can appear multiple times).
The third line consists of n integer numbers, the ith number indicating the preference value P i(0 ≤ P i ≤ 10 15) of the i-th province.
Then n - 1 lines follow. Each line consists of two integers x, y, indicating there is a road connecting province x and province y.
 

Output
For each test case, print a number indicating the number of routes that satisfy the requirement.
 

Sample Input
 
  
5 3 2 3 5 2500 200 9 270000 27 4 2 3 5 2 5 4 1
 

Sample Output
 
  
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;
}


转载于:https://www.cnblogs.com/mypsq/p/4348204.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值