人生的第一道树分治,要是早点学我南京赛就不用那么挫了,树分治的思路其实很简单,就是对子树找到一个重心(Centroid),实现重心分解,然后递归的解决分开后的树的子问题,关键是合并,当要合并跨过重心的两棵子树的时候,需要有一个接近O(n)的方法,因为f(n)=kf(n/k)+O(n)解出来才是O(nlogn).在这个题目里其实就是将第一棵子树的集合里的每个元素,判下有没符合条件的,有就加上,然后将子树集合压进大集合,然后继续搞第二棵乃至第n棵.我的过程用了map,合并是nlogn的所以代码速度颇慢,大概6s,题目时限10s,可以改成hash应该会快许多,毕竟用map实在太慢,用vector也可以,具体可以参见挑战程序设计竞赛代码.下面的代码查找重心用了挑战的代码.

#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<map>
#include<vector>
#define maxv 50000
#define ll long long
using namespace std; int n,k;
vector<int> G[maxv+50];
ll val[maxv+50];
ll prime[maxv+50];
ll convert_three(ll v)
{
ll bas=1;ll res=0;
for(int i=0;i<k;++i){
int num=0;
while(v%prime[i]==0){
v/=prime[i];
num++;
}
num%=3;res+=num*bas;
bas*=3;
}
return res;
} ll xor(ll x,ll y)
{
ll res=0;ll bas=1;
for(int i=0;i<k;++i){
res+=((x%3)+(y%3))%3*bas;
x/=3;y/=3;
bas*=3;
}
return res;
} ll inv(ll x)
{
ll res=0;ll bas=1;
for(int i=0;i<k;++i){
res+=((3-(x%3))%3)*bas;
x/=3;
bas*=3;
}
return res;
} void print(ll x){
while(x){
cout<<x%3;
x/=3;
}
cout<<endl;
} bool centroid[maxv+50];
int ssize[maxv+50];
int ans; map<ll,int> sta;
map<ll,int>::iterator it;
int compute_ssize(int v,int p)
{
int c=1;
for(int i=0;i<G[v].size();++i){
int w=G[v][i];
if(w==p||centroid[w]) continue;
c+=compute_ssize(G[v][i],v);
}
ssize[v]=c;
return c;
} pair<int,int> search_centroid(int v,int p,int t)
{
pair<int,int> res=make_pair(INT_MAX,-1);
int s=1,m=0;
for(int i=0;i<G[v].size();++i){
int w=G[v][i];
if(w==p||centroid[w]) continue;
res=min(res,search_centroid(w,v,t));
m=max(m,ssize[w]);
s+=ssize[w];
}
m=max(m,t-s);
res=min(res,make_pair(m,v));
return res;
} void enumerate_mul(int v,int p,ll d,map<ll,int> &ds)
{
if(ds.count(d)) ds[d]++;
else ds[d]=1;
for(int i=0;i<G[v].size();++i){
int w=G[v][i];
if(w==p||centroid[w]) continue;
enumerate_mul(w,v,xor(d,val[w]),ds);
}
} void solve(int v)
{
compute_ssize(v,-1);
int s=search_centroid(v,-1,ssize[v]).second;
centroid[s]=true;
for(int i=0;i<G[s].size();++i){
if(centroid[G[s][i]]) continue;
solve(G[s][i]);
}
sta.clear();
sta[val[s]]=1;map<ll,int> tds;
for(int i=0;i<G[s].size();++i){
if(centroid[G[s][i]]) continue;
tds.clear();
enumerate_mul(G[s][i],s,val[G[s][i]],tds);
it=tds.begin();
while(it!=tds.end()){
ll rev=inv((*it).first);
if(sta.count(rev)){
ans+=sta[rev]*(*it).second;
}
++it;
}
it=tds.begin();
while(it!=tds.end()){
ll vv=xor((*it).first,val[s]);
if(sta.count(vv)){
sta[vv]+=(*it).second;
}
else{
sta[vv]=(*it).second;
}
++it;
}
}
centroid[s]=false;
} int main()
{
while(cin>>n>>k){
ans=0;
for(int i=0;i<k;++i){
scanf("%I64d",&prime[i]);
}
G[0].clear();
for(int i=1;i<=n;++i){
scanf("%I64d",&val[i]);
val[i]=convert_three(val[i]);
if(val[i]==0) ans++;
//print(val[i]);
G[i].clear();
}
int u,v;
for(int i=0;i<n-1;++i){
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
memset(centroid,0,sizeof(centroid));
solve(1);
printf("%d\n",ans);
}
return 0;
}

HDU4670 Cube number on a tree 树分治的更多相关文章

  1. HDU4670 cube number on a tree(点分治+三进制加法)

    The country Tom living in is famous for traveling. Every year, many tourists from all over the world ...

  2. [hdu4670 Cube number on a tree]点分治

    题意:给一个N个带权节点的树,权值以给定的K个素数为因子,求路径上节点乘积为立方数的路径条数 思路:立方数的性质是每个因子的个数为3的倍数,那么每个因子只需要保存0-2三个状态即可,然后路径就可以转化 ...

  3. HDU 4670 Cube number on a tree ( 树的点分治 )

    题意 : 给你一棵树 . 树的每一个结点都有一个权值 . 问你有多少条路径权值的乘积是一个全然立方数 . 题目中给了你 K 个素数 ( K <= 30 ) , 全部权值都能分解成这k个素数 思路 ...

  4. 【点分治】【map】【哈希表】hdu4670 Cube number on a tree

    求树上点权积为立方数的路径数. 显然,分解质因数后,若所有的质因子出现的次数都%3==0,则该数是立方数. 于是在模意义下暴力统计即可. 当然,为了不MLE/TLE,我们不能存一个30长度的数组,而要 ...

  5. hdu 4670 Cube number on a tree(点分治)

    Cube number on a tree Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/ ...

  6. 【BZOJ-1468】Tree 树分治

    1468: Tree Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1025  Solved: 534[Submit][Status][Discuss] ...

  7. HDU 4812 D Tree 树分治+逆元处理

    D Tree Problem Description   There is a skyscraping tree standing on the playground of Nanjing Unive ...

  8. POJ 1741 Tree 树分治

    Tree     Description Give a tree with n vertices,each edge has a length(positive integer less than 1 ...

  9. POJ 1741.Tree 树分治 树形dp 树上点对

    Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 24258   Accepted: 8062 Description ...

随机推荐

  1. hdu 1874 畅通工程续 Dijkstra

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1874 题目分析:输入起点和终点,顶点的个数,已连通的边. 输出起点到终点的最短路径,若不存在,输出-1 ...

  2. 《samba搭建win客户端和linux客户端的区别》

    samba服务的搭建 客户的使用系统的不同也导致测试结果的不同. linux系统客户端: security = user or share smbclient -L //192.168.7.113/w ...

  3. NaN 和 Infinity

    using Fasterflect; using System; using System.Collections.Generic; using System.Linq; using System.R ...

  4. postgresql 开启远程访问

    1.如果服务器启用了防火墙,需要在防火墙上开启 5432 端口. 2.修改 PostgreSQL 配置文件 postgresql.conf.postgresql.conf,Linux 配置文件所在路径 ...

  5. hadoop自动安装的脚本与步骤

    最近要在10几台机器上安装hadoop.对于这种繁复而重复的工作,一步步的打命令行,对于程序员来说是一件不能忍的事情.所以我就琢磨着怎么写一个脚本来自动安装hadoop. 任务: 在10几台机器上中的 ...

  6. 浅谈 WPF控件

    首先我们必须知道在WPF中,控件通常被描述为和用户交互的元素,也就是能够接收焦点并响应键盘.鼠标输入的元素.我们可以把控件想象成一个容器,容器里装的东西就是它的内容.控件的内容可以是数据,也可以是控件 ...

  7. Microsoft AzureStorageAccount for Powershell

    使用Powershell 创建的存储账户,注意StorageAccountName只能使用小写字母以及数字, -Location参考http://www.cnblogs.com/SignalTips/ ...

  8. .NET Framework 3.5 安装错误:0x800F0906、0x800F081F、0x800F0907

    使用Add-WindowsFeature 照成的问题 I get the failure below..  If I pick the Server 2012 R2 image from 8/15/2 ...

  9. unpipc.h&unpipc.c

    unpipc.h #ifndef _UNPIPC_H #define _UNPIPC_H #include <stdio.h> #include <unistd.h> #inc ...

  10. linux信号量之进程间同步

    概念 linux信号量: 允许多个线程同时进入临界区,可以用于进程间的同步. 和互斥锁(mutex)的区别: 互斥锁只允许一个线程进入临界区. 所在头文件: semaphore.h 主要函数 初始化函 ...