钦点

题目

钦点

题意

给定n个数,两个数之间有边当且仅当两个数的\(gcd\)为合数。求删去一个节点后,最大连通块的大小的最小值是多少。

题解

对没一个可以被分解为两个素数的乘积的数建虚点。
对于一个点, 向他的权值的因数中可以被分解为两个素数的乘积的数的虚点连边。
于是欧拉筛预处理每个数的最小质因子, 然后对于每个点的权值, \(O({因子个数}^2)\)枚举连边即可, 注意处理平方数。
注意虚点不能被计入答案。

显然是去掉最大连通块中的一个割点。
于是在最大连通块上求割点,对于每个割点,求出去掉它之后的最大连通块大小。
最后答案和次大联通块的大小取\(max\)

代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <vector>
#include <algorithm>

using namespace std;


const int N = 10000010, M = 20000010;

const int Lim = 10000000;


struct edge
{
	int from, to;
	edge() { }
	edge(int _1, int _2) : from(_1), to(_2) { }
} edges[M];

int head[N], nxt[M], tot;

inline void init()
{
	memset(head, -1, sizeof(head));
	tot = 0;
}

inline void add_edge(int x, int y)
{
	edges[tot] = edge(x, y);
	nxt[tot] = head[x];
	head[x] = tot++;
	edges[tot] = edge(y, x);
	nxt[tot] = head[y];
	head[y] = tot++;
}


int n;

int w[Lim / 100 + 10];


int pri[Lim / 5 + 10], rec[Lim + 10], total;
bool ispri[Lim + 10];

void pre()
{
	ispri[1] = 1;
	for (int i = 2; i <= Lim; i++)
	{
		if (!ispri[i]) { pri[++total] = i; rec[i] = i; }
		for (int j = 1; j <= total && pri[j] * i <= Lim; j++)
		{
			ispri[i * pri[j]] = 1;
			rec[i * pri[j]] = pri[j];
			if (i % pri[j] == 0) break;
		}
	}
}


bool vis[N];

int dfs(int x)
{
	vis[x] = 1;
	int siz = (x <= n ? 1 : 0);
	for (int i = head[x]; ~i; i = nxt[i])
	{
		edge & e = edges[i];
		if (!vis[e.to])
			siz += dfs(e.to);
	}
	return siz;
}


int low[N], dfn[N], dfs_clock;

int siz[N];

int all;

int Ans;

bool bo[N];

void Tarjan(int x, int fa)
{
	low[x] = dfn[x] = ++dfs_clock;
	siz[x] = (x <= n ? 1 : 0);
	int num = (x <= n ? 1 : 0), maxv = 0;
	
	int s = 0;
	
	for (int i = head[x]; ~i; i = nxt[i])
	{
		edge & e = edges[i];
		if (e.to != fa)
		{
			s++;
			if (!dfn[e.to])
			{
				Tarjan(e.to, x);
				siz[x] += siz[e.to];
				low[x] = min(low[x], low[e.to]);
				if (low[e.to] >= dfn[x])
				{
					bo[x] = 1;
					num += siz[e.to];
					maxv = max(maxv, siz[e.to]);
				}
			}
			else
				low[x] = min(low[x], dfn[e.to]);
		}
	}
	maxv = max(maxv, all - num);
	
	if (!fa && s <= 1) bo[x] = 0;
	if (x <= n && bo[x]) Ans = min(Ans, maxv);
}


vector<int> vec[Lim / 100 + 10];

int id[Lim + 10];


int main()
{
	pre();
	
	int T;
	
	scanf("%d", &T);
	
	while (T--)
	{
		scanf("%d", &n);
		
		init();
		
		int cid = n;
		
		for (int i = 1; i <= n; i++)
			scanf("%d", &w[i]);
		
		for (int i = 1; i <= n; i++)
		{
			int x = w[i];
			int tot = 0;
			static int num[110], sum[110];
			while (x > 1)
			{
				if (num[tot] != rec[x])
					num[++tot] = rec[x], sum[tot] = 0;
				sum[tot]++;
				x /= rec[x];
			}
			vec[i].clear();
			for (int j = 1; j <= tot; j++)
			{
				for (int k = j+1; k <= tot; k++)
				{
					if (num[j] * num[k] > Lim) continue;
					vec[i].push_back(num[j] * num[k]);
					if (!id[num[j] * num[k]]) id[num[j] * num[k]] = ++cid;
					add_edge(i, id[num[j] * num[k]]);
				}
				if (sum[j] > 1)
				{
					if (num[j] * num[j] > Lim) continue;
					vec[i].push_back(num[j] * num[j]);
					if (!id[num[j] * num[j]]) id[num[j] * num[j]] = ++cid;
					add_edge(i, id[num[j] * num[j]]);
				}
			}
		}
		
		
		int num1 = 0, num2 = 0;
		
		for (int i = 1; i <= cid; i++)
			if (!vis[i])
			{
				all = dfs(i);
				if (all >= num1)
				{
					if (all > num1) Ans = all - 1;
					num2 = num1, num1 = all;
				}
				else num2 = max(num2, all);
				if (all == num1) Tarjan(i, 0);
			}
		
		printf("%d\n", max(num2, Ans));
		
		memset(low, 0, sizeof(int) * (cid + 1));
		memset(dfn, 0, sizeof(int) * (cid + 1));
		memset(vis, 0, sizeof(int) * (cid + 1));
		memset(siz, 0, sizeof(int) * (cid + 1));
		memset(bo, 0, sizeof(int) * (cid + 1));
		dfs_clock =  0;
		Ans = 0;
		all = 0;
		for (int i = 1; i <= n; i++)
		{	for (auto u : vec[i]) id[u] = 0;
			vec[i].clear();
		}
	}
	
	return 0;
}
posted @ 2018-11-26 21:21  EZ_WYC  阅读(225)  评论(0编辑  收藏  举报