传送门:Gym - 101853B
给你一堆人,分男女,每个人有个值,单独一个人可以一组,一男一女也可以凑成一组,但是要求每一对男女的值的gcd不能等于1。 同时题目保证给出的数中任意三个人的gcd都等于1。求最少分成多少组。人数小于等于1e4,一共100组数据
求最少分成多少组,就是求最大匹配嘛,这个很容易想到。(ps:题意读错了另说,例如我的某位可爱的沙雕队友)n有1e4,那就isap搞一发。但是怎么建图呢。
gcd不等于1,首先想到的就是朴素算法,n^2复杂度,虽然cf评测机好,但是1e8*1e2怎么也肯定t了。
然后想到剪枝,搞两个队列,每次枚举点,一旦gcd不得1,就把两个数除以gcd,如果除完得1了,就不再进把他进队了。这么搞了一发,又t。毕竟最坏结果还是1e8。
然后就想到先搞一发,然后再唯一素数分解。
然后咋办呢,把每个素数都搞一个vector,把所有数唯一素数分解出来的素数,把这个数的编号噎到每个他有的素数的vector里,然后枚举每个vector,暴力匹配一下。这样复杂度是o(edge)的。而且他说了,任意三个gcd都得1,这样每个vector的size不超过2,复杂度就及其科学了。同时注意一下,加边的时候,把x和y重新标号一下,连接的时候,边方向别错了,我重标号之后就是小的是连超源,大的连超汇,这样就是min连max就好了。
ps:这破题代码真长。
然后在我刚写完这篇博客之后,大佬告诉我一个更快的。。。更好的写法orz,这个算法叫做直接暴力。。。
#include
#define F first
#define S second
#define endl "\n"
#define rep(i , a , b) for (int i=a; i<(b); i++)
#define re0(i , a) for (int i=0; i<(a); i++)
using namespace std;
typedef pair<int, int> ii;
typedef long long ll;
typedef long double ld;
const int mod = 7 + 1e9;
const int N = 5 + 1e4;
pair<char, int>a[N];
bool b[N];
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
int t;
cin >> t;
while (t--) {
memset(b, 0, sizeof b);
int n, i, j = 0, ans = 0;
cin >> n;
re0(i, n)
cin >> a[i].S;
re0(i, n)
cin >> a[i].F;
sort(a, a + n);
while (a[j].F == 'F') j++;
int l;
for (i = 0; i < j; ++i) {
for (l = j; l < n; ++l) {
if (!b[l])
if (__gcd(a[l].S, a[i].S) != 1) {
ans++;
b[l] = 1;
b[i] = 1;
break;
}
}
}
re0(i, n) {
if (!b[i])ans++;
}
cout << ans << endl;
}
}
先贴一发素数筛和唯一素数分解的模板
int phi[maxn], prime[maxn], tot, fa[maxn];
bool notp[maxn];
void getphi(int M) {
phi[1] = 1;
for (int i = 2; i <= M; i++) {
if (!notp[i]) {
prime[++tot] = i;
fa[i] = tot;
phi[i] = i - 1;//i是素数
}
for (int j = 1; j <= tot && i * prime[j] <= M; j++) {
notp[i * prime[j]] = 1;//不是素数
if (i % prime[j] == 0) {// 如果i mod p==0,那么φ( i*p )=p*φ( i )
phi[i * prime[j]] = phi[i] * prime[j];
break;//筛素数省时
}
// 若i mod p≠0,那么φ(i*p)=φ(i)*(p-1)
else phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
return;
}
void prime_explode(ll x, vector &save) {
ll w = x;
save.clear();
for (int i = 1; i <= tot && (ll) prime[i] * prime[i] <= w; i++) {
if (x % prime[i] == 0) {
pii now(prime[i], 0);
while (x % prime[i] == 0) {
x /= prime[i];
now.second++;
}
save.push_back(now);
}
}
if (x != 1) {
save.push_back(pii(x, 1));
}
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#pragma comment(linker, "/STACK:102400000,102400000")
#define fir first
#define sec second
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#define clr(x) memset(x,0,sizeof(x))
#define cld(x) memset(x,-1,sizeof(x))
#define clx(x) memset(x,63,sizeof(x))
#define cln(x) memset(x,-64,sizeof(x))
#define rush() int T;rd.read(T);while(T--)
#define pi 3.1415926
#define VM 100047
#define EM 400047
//#define rd(x) scanf("%d",&x);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair pll;
const int inf = 0x3f3f3f3f;
const ll llf = 0x3f3f3f3f3f3f3f3f;
const int maxn = (int) 1e6 + 7;
const double eps = 1e-10;
const ll mod1 = (int) 1e9 + 7;
const ll mod2 = 998244353;
const ll has = 99959;
const int dx[] = {0, 1, 0, -1};
const int dy[] = {1, 0, -1, 0};
struct ISAP {
struct E {
int to, frm, nxt, cap;
} edge[EM];
int head[VM], e, n, m, src, des;
int dep[VM], gap[VM];
int que[VM];
void init(int _n, int _s, int _r) {
n = _n;
src = _s;
des = _r;
e = 0;
cld(head);
//clr(edge);
}
void AddEdge(int cu, int cv, int cw) {
edge[e].frm = cu;
edge[e].to = cv;
edge[e].cap = cw;
edge[e].nxt = head[cu];
head[cu] = e++;
edge[e].frm = cv;
edge[e].to = cu;
edge[e].cap = 0;
edge[e].nxt = head[cv];
head[cv] = e++;
}
void BFS() {
memset(dep, -1, sizeof(dep));
memset(gap, 0, sizeof(gap));
gap[0] = 1;
int front = 0, rear = 0;
dep[des] = 0;
que[rear++] = des;
int u, v;
while (front != rear) {
u = que[front++];
front = front % VM;
for (int i = head[u]; i != -1; i = edge[i].nxt) {
v = edge[i].to;
if (edge[i].cap != 0 || dep[v] != -1)
continue;
que[rear++] = v;
rear = rear % VM;
++gap[dep[v] = dep[u] + 1];
}
}
}
int cur[VM], stack[VM];
int Max_flow() {
int res = 0;
BFS();
int top = 0;
memcpy(cur, head, sizeof(head));
int u = src, i;
while (dep[src] < n) {
if (u == des) {
int temp = inf, inser = n;
for (i = 0; i != top; ++i)
if (temp > edge[stack[i]].cap) {
temp = edge[stack[i]].cap;
inser = i;
}
for (i = 0; i != top; ++i) {
edge[stack[i]].cap -= temp;
edge[stack[i] ^ 1].cap += temp;
}
res += temp;
top = inser;
u = edge[stack[top]].frm;
}
if (u != des && gap[dep[u] - 1] == 0)
break;
for (i = cur[u]; i != -1; i = edge[i].nxt)
if (edge[i].cap != 0 && dep[u] == dep[edge[i].to] + 1)
break;
if (i != -1) {
cur[u] = i;
stack[top++] = i;
u = edge[i].to;
} else {
int min = n;
for (i = head[u]; i != -1; i = edge[i].nxt) {
if (edge[i].cap == 0)
continue;
if (min > dep[edge[i].to]) {
min = dep[edge[i].to];
cur[u] = i;
}
}
--gap[dep[u]];
++gap[dep[u] = min + 1];
if (u != src)
u = edge[stack[--top]].frm;
}
}
return res;
}
} sap;
template<class T>
class in_out {
public:
inline bool read(T &ret) {
char c;
int sgn;
if (c = static_cast<char>(getchar()), c == EOF) {
return false;
}
while (c != '-' && (c < '0' || c > '9')) {
c = static_cast<char>(getchar());
}
sgn = (c == '-') ? -1 : 1;
ret = (c == '-') ? 0 : (c - '0');
while (c = static_cast<char>(getchar()), c >= '0' && c <= '9') {
ret = ret * 10 + (int) (c - '0');
}
ret *= sgn;
return true;
}
void out(T a) {
if (a < 0) {
putchar('-');
a = -a;
}
if (a >= 10)
out(a / 10);
putchar(a % 10 + '0');
}
};
in_out<int> rd;
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
struct rec {
int x, id;
char ch[2];
rec(int _x = 0, int _id = 0) {
x = _x;
id = _id;
}
} a[maxn];
int b[maxn], c[maxn];
int n;
int phi[maxn], prime[maxn], tot, fa[maxn];
bool notp[maxn];
void getphi(int M) {
phi[1] = 1;
for (int i = 2; i <= M; i++) {
if (!notp[i]) {
prime[++tot] = i;
fa[i] = tot;
phi[i] = i - 1;//i是素数
}
for (int j = 1; j <= tot && i * prime[j] <= M; j++) {
notp[i * prime[j]] = 1;//不是素数
if (i % prime[j] == 0) {// 如果i mod p==0,那么φ( i*p )=p*φ( i )
phi[i * prime[j]] = phi[i] * prime[j];
break;//筛素数省时
}
// 若i mod p≠0,那么φ(i*p)=φ(i)*(p-1)
else phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
return;
}
void prime_explode(ll x, vector &save) {
ll w = x;
save.clear();
for (int i = 1; i <= tot && (ll) prime[i] * prime[i] <= w; i++) {
if (x % prime[i] == 0) {
pii now(prime[i], 0);
while (x % prime[i] == 0) {
x /= prime[i];
now.second++;
}
save.push_back(now);
}
}
if (x != 1) {
save.push_back(pii(x, 1));
}
}
vector<int> vec[maxn];
vector v;
int main() {
std::ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
getphi(maxn);
rush() {
clr(vec);
rd.read(n);
int mx = 0;
for (int i = 1; i <= n; i++) {
rd.read(a[i].x);
mx = max(mx, a[i].x);
}
int num1 = 0;
for (int i = 1; i <= n; i++) {
scanf("%s", a[i].ch);
if (a[i].ch[0] == 'M')
num1++;
}
int s = n + 10;
int t = s + 1;
sap.init(t + 10, s, t);
int numb = 0;
int numc = num1;
for (int i = 1; i <= n; i++) {
v.clear();
if (a[i].ch[0] == 'M') {
numb++;
prime_explode(a[i].x, v);
for (auto &x : v) {
vec[fa[x.first]].push_back(numb);
}
sap.AddEdge(s, numb, 1);
} else {
numc++;
prime_explode(a[i].x, v);
for (auto &x : v) {
vec[fa[x.first]].push_back(numc);
}
sap.AddEdge(numc, t, 1);
}
}
for (int i = 1; i <= mx; i++) {
if (!vec[i].empty()) {
for (int j = 0; j < vec[i].size() - 1; j++) {
for (int k = j + 1; k < vec[i].size(); k++) {
sap.AddEdge(min(vec[i][j], vec[i][k]), max(vec[i][j], vec[i][k]), 1);
}
}
}
}
printf("%d\n", n - sap.Max_flow());
}
return 0;
}