钦点
题目
题意
给定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;
}