您的位置:首页 > 理论基础 > 计算机网络

HDU 5468 Puzzled Elena (2015年上海赛区网络赛A题)

2015-10-01 22:54 549 查看
1.题目描述:点击打开链接

2.解题思路:本题利用dfs序+容斥原理+前缀和性质解决。题目中要求每个结点,和多少个它的子结点互素。如果每次为了求一个点去跑一遍dfs,复杂度将是O(N(N+M))。一定会超时。因此需要深入加以分析。注意到n的范围是10^5以内的,因此可以事先用线性筛求出每个数含有哪些素因子。接下来,我们尝试利用dfs序来求解。设num[i]表示遍历到当前结点时候,含有因数i(注意,不一定是素数)的结点个数。可以发现,如果第一次遍历到结点u,含有u的因数的个数为a,那么第二次遍历到u时候,含有u的因数的个数变成了b,那么b-a就是u的子树中,含有u的因数的结点个数,即和u不互素的结点个数。用总的结点数减掉这部分,即得到了和u互素的结点个数。这正是用了前缀和的性质。

那么,如何求解有当前有多个结点含有u的因数呢?可以利用容斥原理求解。因为我们已经预处理出来了所有数的素因数,假设有len个素因数,由于“含有”即表示只要有1个即可。因此结果就是{只含有1种素因子的个数}-{只含有2种素因子的个数}+{只含有3个素因子的个数}-...+(-1)^(n-1){含有n个素因子的个数}。这恰好就是容斥原理。至此,问题得以解决。

3.代码:

#include<iostream>
#include<algorithm>
#include<cassert>
#include<string>
#include<sstream>
#include<set>
#include<bitset>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<list>
#include<complex>
#include<functional>
using namespace std;

#define me(s) memset(s,0,sizeof(s))
#define rep(i,n) for(int i=0;i<(n);i++)
#define pb push_back
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair <int,int> P;

const int N = 100000 + 10;
vector<int>g
;
int num
;
int val
;
int L
, R
, ans
;
struct Edge
{
int to, next;
}edge[N << 2];
int head
;
int e, n;

void init()
{
for (int i = 2; i<N; i++)
{
if (!g[i].empty())continue;
for (int j = i; j<N; j += i)
g[j].pb(i);
}
}

void addedge(int u, int v)
{
edge[e].to = v;
edge[e].next = head[u];
head[u] = e++;
}

int bitcount(int x) { return !x ? 0 : bitcount(x / 2) + (x & 1); }

int calc(int x, int val)//计算当前有多少个结点含有x的因数,val=0表示不更新num数组,val=1表示用x来更新num数组
{
int len = g[x].size();
int res = 0;
for (int i = 1; i<1 << len; i++)
{
int t = 1;
for (int j = 0; j<len; j++)
if (i >> j & 1)t *= g[x][j];
if (bitcount(i) & 1)res += num[t];
else res -= num[t];
num[t] += val;
}
return res;
}

int dfs(int u, int fa)//dfs返回的是u这棵子树的总结点数
{
int cnt = 0;
L[u] = calc(val[u], 0);
for (int i = head[u]; ~i; i = edge[i].next)
{
int v = edge[i].to;
if (v != fa)
cnt += dfs(v, u);
}
R[u] = calc(val[u], 1);
ans[u] = cnt - (R[u] - L[u]);
if (val[u] == 1)ans[u]++; //注意!1和自身也互素
return cnt + 1;
}

int main()
{
init();
int n;
int kase = 0;
while (~scanf("%d", &n))
{
e = 0;
memset(head, -1, sizeof(head));
me(num);
int u, v;
for (int i = 1; i<n; i++)
{
scanf("%d%d", &u, &v);
u--, v--;
addedge(u, v);
addedge(v, u);
}
for (int i = 0; i<n; i++)
scanf("%d", &val[i]);
dfs(0, -1);
printf("Case #%d:", ++kase);
for (int i = 0; i<n; i++)
printf(" %d", ans[i]);
puts("");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dfs序 容斥原理