虽然这道题数据有一点水 但还是觉得很有趣,是在想不出来的情况下还是可以多种方法试着苟一下(命)分嘛~
3 c
3.1 问题描述
两个数a 和b (a < b) 被称为质数相关,是指a×p = b,这里p 是一个质数。一个集合S
被称为质数相关,是指S 中存在两个质数相关的数,否则称S 为质数无关。如2; 8; 17 质数无
关,但2; 8; 16, 3; 6 质数相关。现在给定一个集合S,问S 的所有质数无关子集中,最大的子
集的大小。
3.2 输入
第一行为一个数T,为数据组数。之后每组数据包含两行。
第一行为N,为集合S 的大小。第二行为N 个整数,表示集合内的数。
3.3 输出
对于每组数据输出一行,即最大的无关子集。
3.4 输入输出样例1
3.4.1 输入样例
3
5
2 4 8 16 32
5
2 3 4 6 9
3
1 2 3
3.4.2 输出样例
3
3
2
3.5 约定和数据范围
集合S 内的数两两不同且范围在1 到500000 之间。
对于40% 的数据, 1 n 15,
对于100% 的数据, 1 T 20, 1 n 1000。
法一:
排序过后把每个值连一条边到比他大并且会被他影响的值,看起来好像是一棵树诶??(可是后来发现有反例可以使它成环,所以是在数据水的时候苟苟分嘛~)然后树规即可(和没有上司的舞会完全一样
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
bool isnot[500005];
int prime[100005], tot, vis[500005], dp[500005][2], a[1005];
int stot, nex[1000005], tov[1000005], h[500005];
void add ( int u, int v ) {
tov[++stot] = v;
nex[stot] = h[u];
h[u] = stot;
}
void init ( ) {
isnot[1] = 1;
for ( int i = 2; i <= 500000; i ++ ) {
if ( !isnot[i] ) {
prime[++tot] = i;
}
for ( int j = 1; j <= tot && i * prime[j] < 500005; j ++ ) {
isnot[i*prime[j]] = 1;
if ( i % prime[j] == 0 ) break;
}
}
}线性筛
void dfs ( int u, int f ) {
vis[u] = 1;
dp[u][1] = 1; dp[u][0] = 0;
for ( int i = h[u]; i; i = nex[i] ) {
int v = tov[i];
if ( vis[v] ) continue;
dfs ( v, u );
dp[u][0] += max ( dp[v][1], dp[v][0] );
dp[u][1] += dp[v][0];
}
}
int main ( ) {
freopen ( "c.in", "r", stdin );
freopen ( "c.out", "w", stdout );
int T;
scanf ( "%d", &T );
init ( );
while ( T -- ) {
memset ( vis, 0, sizeof ( vis ) );
memset ( h, 0, sizeof ( h ) );
scanf ( "%d", &n );
for ( int i = 1; i <= n; i ++ )
scanf ( "%d", &a[i] );
sort ( a + 1, a + 1 + n );
for ( int i = 1; i <= n; i ++ )
for ( int j = i + 1; j <= n; j ++ )
if ( a[j] % a[i] == 0 ) {
if ( !isnot[a[j]/a[i]] ) {
add ( a[i], a[j] );
add ( a[j], a[i] );
}
}
int ans = 0;
for ( int i = 1; i <= n; i ++ )
if ( !vis[a[i]] ) {
dfs ( a[i], a[i] );
ans += max ( dp[a[i]][0], dp[a[i]][1] );
}
printf ( "%d\n", ans );
}
return 0;
}
法二:正解!!
二分图匹配!把质因子个数不同按奇偶性分成两边(同奇或者同偶一定不会互相影响)然后之间能互相影响的相互建边,最大匹配中的每一组都只能选择其中一个,用总点数减去最大匹配就是答案。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
bool isnot[500005];
int prime[100005], tot, vis[500005], dp[500005][2], a[1005];
int connect[500005], qaq[500005], qwq[500005], qwqwq, qaqaq;
int stot, nex[1000005], tov[1000005], h[500005];
void add ( int u, int v ) {
tov[++stot] = v;
nex[stot] = h[u];
h[u] = stot;
}
void init ( ) {
isnot[1] = 1;
for ( int i = 2; i <= 500000; i ++ ) {
if ( !isnot[i] ) {
prime[++tot] = i;
}
for ( int j = 1; j <= tot && i * prime[j] < 500005; j ++ ) {
isnot[i*prime[j]] = 1;
if ( i % prime[j] == 0 ) break;
}
}
}
int count ( int x ) {
int cnt = 0;
for ( int i = 1; i <= tot && x != 1; i ++ ) {
while ( x % prime[i] == 0 ) {
cnt ++; x /= prime[i];
}
}
return cnt;
}
bool CUT ( int u ) {
for ( int i = h[u]; i; i = nex[i] ) {
int v = tov[i];
if ( !vis[v] ) {
vis[v] = 1;
if ( CUT ( connect[v] ) || !connect[v] ) {
connect[v] = u;
return true;
}
}
}
return false;
}
int main ( ) {
freopen ( "c.in", "r", stdin );
freopen ( "c.out", "w", stdout );
int T;
scanf ( "%d", &T );
init ( );
while ( T -- ) {
memset ( vis, 0, sizeof ( vis ) );
memset ( h, 0, sizeof ( h ) );
memset ( qaq, 0, sizeof ( qaq ) );
memset ( qwq, 0, sizeof ( qwq ) );
memset ( connect, 0, sizeof ( connect ) );
qwqwq = qaqaq = 0;
scanf ( "%d", &n );
for ( int i = 1; i <= n; i ++ )
scanf ( "%d", &a[i] );
sort ( a + 1, a + 1 + n );
for ( int i = 1; i <= n; i ++ ) {
int num = count ( a[i] );
if ( num % 2 ) qwq[++qwqwq] = a[i];
else qaq[++qaqaq] = a[i];
}
for ( int i = 1; i <= qwqwq; i ++ )
for ( int j = 1; j <= qaqaq; j ++ )
if ( qaq[j] % qwq[i] == 0 ) {
if ( !isnot[qaq[j]/qwq[i]] ) {
add ( qwq[i], qaq[j] );
}
} else if ( qwq[i] % qaq[j] == 0 ) {
if ( !isnot[qwq[i]/qaq[j]] ) {
add ( qwq[i], qaq[j] );
}
}//////注意两边的大小
int ans = 0;
for ( int i = 1; i <= qwqwq; i ++ ) {
for ( int j = 1; j <= n; j ++ )
vis[a[j]] = 0;
if ( CUT ( qwq[i] ) )
ans += 1;
}
printf ( "%d\n", n - ans );
}
return 0;
}
(为什么今天要发题解?因为不想刷题叻!