HDU 5468 Puzzled Elena(DFS序+容斥原理)
2015-09-30 21:23
363 查看
题目链接:传送门
题意:
给定一棵树,求这个节点的所有子树中包括他本身与它互质的节点的个数。
分析:转自传送门
因为每个数的大小<=100000 这个范围内 2*3*5*7*11*13 *17> 100000 最多只有 6 个素因子。
当我们知道这个怎么处理以后,我们可以利用dfs序,解决这个问题
我们求当前这个节点的答案时,用容斥搞就是:以这个节点为根的树的大小 - 有1个素因子和他相同的节点个数 + 有2个素因子和他相同的个数 - 有3个素因子和他相同的个数 ....
那么问题来了,我们如何求出有多少个 有1个素因子和他相同的个数,有2个素因子和他相同的个数 ,,,,,
我们维护一个数fac[]数组,fac[i] 代表包含因子i的节点个数。
那么在这颗树中,进入这颗树之前求一下(有1个素因子和他相同的节点个数,有2个素因子和他相同的节点个数.....),离开这颗树的时候再求一下(有1个素因子和他相同的节点个数,有2个素因子和他相同的节点个数.....),他们的差便是我们需要的(子树中的和他有关系的信息)。
到这里,问题就解决了,容斥版的做法 时间复杂度 O(n*2^6 + nlogn) :
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int fac[maxn];
int w[maxn];
int ans[maxn];
struct Graph{
vector<int >vc[maxn];
void init(){
for(int i=0;i<maxn;i++)
vc[i].clear();
}
void add(int u,int v){
vc[u].push_back(v);
}
}G1,G2;
void prepare(){
G1.init();
for(int i=2;i<maxn;i++){
if(G1.vc[i].size())
continue;
for(int j=i;j<maxn;j+=i)
G1.add(j,i);
}
}
int calc(int u,int x){
int ans = 0,n=G1.vc[u].size();
for(int i=1;i<(1<<n);i++){
int tot=1,cnt=0;
for(int j=0;j<n;j++){
if((1<<j)&i){
cnt++;
tot=tot*G1.vc[u][j];
}
}
if(cnt&1)
ans = ans+fac[tot];
else
ans = ans-fac[tot];
fac[tot]+=x;
}
return ans;
}
int dfs(int u,int pre){
int cnt = 0;
int L = calc(w[u],0);
for(int i=0;i<G2.vc[u].size();i++){
int v = G2.vc[u][i];
if(v==pre) continue;
cnt+=dfs(v,u);
}
int R = calc(w[u],1);
ans[u]=cnt-(R-L);
if(w[u]==1) ans[u]++;
return cnt+1;
}
int main()
{
prepare();
int n,cas=1;
while(~scanf("%d",&n)){
G2.init();
memset(fac,0,sizeof(fac));
int u,v;
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
G2.add(u,v);
G2.add(v,u);
}
for(int i=1;i<=n;i++)
scanf("%d",w+i);
dfs(1,0);
printf("Case #%d: ",cas++);
for(int i=1;i<=n;i++){
printf("%d%c",ans[i],i==n? '\n':' ');
}
}
return 0;
}
题意:
给定一棵树,求这个节点的所有子树中包括他本身与它互质的节点的个数。
分析:转自传送门
因为每个数的大小<=100000 这个范围内 2*3*5*7*11*13 *17> 100000 最多只有 6 个素因子。
当我们知道这个怎么处理以后,我们可以利用dfs序,解决这个问题
我们求当前这个节点的答案时,用容斥搞就是:以这个节点为根的树的大小 - 有1个素因子和他相同的节点个数 + 有2个素因子和他相同的个数 - 有3个素因子和他相同的个数 ....
那么问题来了,我们如何求出有多少个 有1个素因子和他相同的个数,有2个素因子和他相同的个数 ,,,,,
我们维护一个数fac[]数组,fac[i] 代表包含因子i的节点个数。
那么在这颗树中,进入这颗树之前求一下(有1个素因子和他相同的节点个数,有2个素因子和他相同的节点个数.....),离开这颗树的时候再求一下(有1个素因子和他相同的节点个数,有2个素因子和他相同的节点个数.....),他们的差便是我们需要的(子树中的和他有关系的信息)。
到这里,问题就解决了,容斥版的做法 时间复杂度 O(n*2^6 + nlogn) :
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int fac[maxn];
int w[maxn];
int ans[maxn];
struct Graph{
vector<int >vc[maxn];
void init(){
for(int i=0;i<maxn;i++)
vc[i].clear();
}
void add(int u,int v){
vc[u].push_back(v);
}
}G1,G2;
void prepare(){
G1.init();
for(int i=2;i<maxn;i++){
if(G1.vc[i].size())
continue;
for(int j=i;j<maxn;j+=i)
G1.add(j,i);
}
}
int calc(int u,int x){
int ans = 0,n=G1.vc[u].size();
for(int i=1;i<(1<<n);i++){
int tot=1,cnt=0;
for(int j=0;j<n;j++){
if((1<<j)&i){
cnt++;
tot=tot*G1.vc[u][j];
}
}
if(cnt&1)
ans = ans+fac[tot];
else
ans = ans-fac[tot];
fac[tot]+=x;
}
return ans;
}
int dfs(int u,int pre){
int cnt = 0;
int L = calc(w[u],0);
for(int i=0;i<G2.vc[u].size();i++){
int v = G2.vc[u][i];
if(v==pre) continue;
cnt+=dfs(v,u);
}
int R = calc(w[u],1);
ans[u]=cnt-(R-L);
if(w[u]==1) ans[u]++;
return cnt+1;
}
int main()
{
prepare();
int n,cas=1;
while(~scanf("%d",&n)){
G2.init();
memset(fac,0,sizeof(fac));
int u,v;
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
G2.add(u,v);
G2.add(v,u);
}
for(int i=1;i<=n;i++)
scanf("%d",w+i);
dfs(1,0);
printf("Case #%d: ",cas++);
for(int i=1;i<=n;i++){
printf("%d%c",ans[i],i==n? '\n':' ');
}
}
return 0;
}
相关文章推荐
- BZOJ 1965: [Ahoi2005]SHUFFLE 洗牌( 数论 )
- 第三次作业——结对编程
- CSS样式适配杂记
- C语言编程需要注意的64位和32机器的区别
- 基本概念的认识
- Frgament onAttach deprecated
- 什么是类型安全
- Map Reduce个数问题
- 帝国cms文章内容tags关键词设置调用方法以及tags静态化
- English power——每天给彼此一个stupid smile
- 第三次作业——结对编程
- 如何使用xcode
- Web学习之——03Apache服务器的高级配置(虚拟主机、status)
- hdu 5458 Stability
- iOS开发之有趣的UI —— MVVM设计模式
- 程序运行原理
- DOS操作系统、常用DOS命令简介
- Android中数据库的创建与增删改查操作
- V离MWare至Openstack至FDIO
- 多界面之间的传值