1. 程式人生 > >HDU 5468 Puzzled Elena (2015年上海賽區網路賽A題)

HDU 5468 Puzzled Elena (2015年上海賽區網路賽A題)

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[N];
int num[N];
int val[N];
int L[N], R[N], ans[N];
struct Edge
{
	int to, next;
}edge[N << 2];
int head[N];
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("");
	}
}