레이블이 14672 윤호는 마법약 도둑인 게시물을 표시합니다. 모든 게시물 표시
레이블이 14672 윤호는 마법약 도둑인 게시물을 표시합니다. 모든 게시물 표시

2017년 12월 27일 수요일

14672 윤호는 마법약 도둑

14672 윤호는 마법약 도둑 https://www.acmicpc.net/problem/14672

윤호는 M개의 N보다 작은 숫자가 적혀져있는 마법약들을 가지고 있다.
마법약은 1을 제외한 약수들중 하나로 추출 할 수 있다.
추출에 성공하려면 추출한 제품들 중에서 어떠한 원료도 공유해서는 안된다.
윤호의 추출할 수 있는 최대 마법약의 개수를 구하는 문제이다.

마법약들을 추출했을 때 모든 마법약들의 원료들이 서로소인 최대 마법약 개수를 구하는 것이다.
gcd를 이용하기에는 구할 방법이 마땅히 없어보였다.

사실상 원료들이 서로소가 되려면 마법약에서 추출할 수 원료들을 소수로 추출하면 된다.
문제는 최대 마법약 개수를 구해야하는데 이분매칭을 이용하면 된다.
네트워크를 나타내면 위와같이 될것이다.
N이 100,000,000이므로 최대 10,000까지의 소수만 구해주면 되는데 여기 실수가 있었다.

10,000보다 큰 수가 들어올 때 10,000보다 작은 소수로 소인수 분해를 하더라도 끝나지 않을 수 있다.
예를들어 104,729와 같은 수이다. 

이런 수는 따로 처리를 해서 저장을 해줘야한다.
밑의 코드에서 소인수 분해를 하고 x > 1인경우가 따로 처리한 경우이다.
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <unordered_map>
#include <queue>
using namespace std;
#define MAX 3500
#define PLUS 2300
#define INF 987654321
int N, M;
int arr[1001];
int pr[10011];
vector<int> prime;
int Size;
vector<int> adj[MAX];
int C[MAX][MAX], f[MAX][MAX];
const int src = MAX - 2, sink = MAX - 1;
int level[MAX], work[MAX];
unordered_map<intint> save;
int cnt = 0;
void init() {
    int L = (int)sqrt(N) + 1;
    for (int n = 2;n*<= L;n++) {
        for (int m = n*n;m <= L;m += n) pr[m] = true;
    }
    for (int n = 2;n <= L;n++if (!pr[n]) prime.push_back(n);
    Size = prime.size();
}
void add_edge(int u, int v, int c) {
    adj[u].push_back(v);
    adj[v].push_back(u);
    C[u][v] = c;
}
bool bfs() {
    memset(level, -1sizeof level);
    queue<int> q;
    q.push(src);
    level[src] = 0;
    while (!q.empty()) {
        int here = q.front();
        q.pop();
        for (auto &next : adj[here]) {
            if (level[next] == -1 && C[here][next] - f[here][next] > 0) {
                level[next] = level[here] + 1;
                q.push(next);
            }
        }
    }
    return level[sink] != -1;
}
int dfs(int here, const int sink, int flow) {
    if (here == sink) return flow;
    for (int &= work[here]; n < adj[here].size(); n++) {
        int next = adj[here][n];
        if (level[next] == level[here] + 1 && C[here][next] - f[here][next] > 0) {
            int get = dfs(next, sink, min(flow, C[here][next] - f[here][next]));
            if (get) {
                f[here][next] += get;
                f[next][here] -= get;
                return get;
            }
        }
    }
    return 0;
}
int main() {
    scanf("%d%d"&N, &M);
    init();
    for (int m = 0, x;m < M;m++) {
        scanf("%d"&x);
        for (int n = 0;n < Size;n++) {
            if (x % prime[n] == 0) {
                add_edge(n, m + PLUS, 1);
                while (x && x % prime[n] == 0) x /= prime[n];
            }
        }
        if (x > 1) {
            if (save[x] == 0) save[x] = ++cnt;
            add_edge(Size + save[x], m + PLUS, 1);
        }
        add_edge(m + PLUS, sink, 1);
    }
    for (int n = 0;n < Size;n++) add_edge(src, n, 1);
    for (int n = 1;n <= cnt;n++) add_edge(src, Size + n, 1);
    int ans = 0;
    while (bfs()) {
        memset(work, 0sizeof work);
        while (1) {
            int get = dfs(src, sink, INF);
            if (!get) break;
            ans += get;
        }
    }
    printf("%d\n", ans);
    return 0;
}
cs