[ARC080F] Prime Flip

https://atcoder.jp/contests/arc080/tasks/arc080_d

Problem Statement

There are infinitely many cards, numbered 1 1 1, 2 2 2, 3 3 3, . . . ... ... Initially, Cards x 1 x_1 x1, x 2 x_2 x2, . . . ... ..., x N x_N xN are face up, and the others are face down.

Snuke can perform the following operation repeatedly:

  • Select a prime p p p greater than or equal to 3 3 3. Then, select p p p consecutive cards and flip all of them.

Snuke’s objective is to have all the cards face down. Find the minimum number of operations required to achieve the objective.

Constraints

  • 1 ≤ N ≤ 100 1 ≤ N ≤ 100 1N100
  • KaTeX parse error: Expected 'EOF', got '&' at position 9: 1 ≤ x_1 &̲lt; x_2 < ..…

Input

Input is given from Standard Input in the following format:

N N N
x 1 x_1 x1 x 2 x_2 x2 . . . ... ... x N x_N xN

Output

Print the minimum number of operations required to achieve the objective.

思路:异或差分,网络流
分三类讨论:
考虑消去 b i = b j = 1 ( i < j ) b_i=b_j=1(i<j) bi=bj=1(i<j),的代价。
2 ∤ ( j − i ) 2∤(j−i) 2(ji) j − i j−i ji 为素数 显然我们只需要 1 次操作。
2 ∣ ( j − i ) 2∣(j−i) 2(ji) j − i = 2 j−i=2 ji=2,我们可以翻转长度 5 和 3 的区间,其他情况根据歌德巴赫猜想我们只需要 2 次操作一定能够消去。
2 ∤ ( j − i ) 2∤(j−i) 2(ji) j − i j−i ji 不为素数(注意包括 1) 与第二种情况同理,但由于我们只能用奇素数,我们需要 3 次操作才能消去。

#include<bits/stdc++.h>

using namespace std;
const int N = 1100, inf = 0x7fffffff;
struct edge {
    int u, v, c, nxt;
};
int head[N], s, t, dep[N], cur[N];
vector<edge> e;
void init() {
//    e.clear();
    memset(head, -1, sizeof head);
}
void add(int u, int v, int c) {
    e.push_back({u, v, c, head[u]});
    head[u] = e.size() - 1;
    e.push_back({v, u, 0, head[v]});
    head[v] = e.size() - 1;
}

bool bfs() {
    queue<int> q;
    q.push(s);
    memset(dep, -1, sizeof dep);
    dep[s] = 1;
    cur[s] = head[s];
    while (q.size()) {
        int u = q.front();
        q.pop();
        for (int i = head[u]; ~i; i = e[i].nxt) {
            int v = e[i].v, c = e[i].c;
            if (dep[v] == -1 && c) {
                dep[v] = dep[u] + 1;
                cur[v] = head[v];
                q.push(v);
            }
        }
    }
    return dep[t] != -1;
}
int dfs(int x, int lim) {
    if (x == t)return lim;
    int flow = 0;
    for (int i = cur[x]; ~i; i = e[i].nxt) {
        int y = e[i].v, c = e[i].c;
        if (dep[y] == dep[x] + 1 && c) {
            int nl = min(c, lim - flow);
            if (!nl)dep[y] = -1;
            int p = dfs(y, nl);
            flow += p;
            e[i].c -= p;
            e[i ^ 1].c += p;
            if (flow == lim)return lim;
        }
    }
    return flow;
}
int dinic() {
    int ans = 0;
    while (bfs()) {
        ans += dfs(s, inf);
    }
    return ans;
}
bool ok(int x) {
    if (x <= 2) return false;
    if (x % 2 == 0) return false;
    for (long long i = 2; i * i <= x; i++) {
        if (x % i == 0) {
            return false;
        }
    }
    return true;
}

int main() {
    int n;
    cin >> n;
    map<int, int> a, b;
    for (int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        a[x] = 1;
    }
    for (auto [x, y]: a) {
        int ax = y;
        int axd = a.count(x - 1);
        int axp = a.count(x + 1);
        b[x] = ax ^ axd;
        b[x + 1] = axp ^ ax;
    }

    vector<int> dian;
    for (auto [x, y]: b) {
        if (y == 1) {
            dian.push_back(x);
//            cout << x << "#\n";
        }
    }
    int dian_num = dian.size();
    s = dian_num * 2 + 1, t = dian_num * 2 + 2;
    init();
    for (int i = 0; i < dian_num; i++) {
        for (int j = i + 1; j < dian_num; j++) {
            if (ok(dian[j] - dian[i])) {
                if (dian[i] % 2 == 0) {
                    add(i, j, 1);
                } else {
                    add(j, i, 1);
                }
            }
        }
    }
    int ji = 0, ou = 0;
    for (int i = 0; i < dian_num; i++) {
        if (dian[i] % 2 == 0) {
            add(s, i, 1);
            ou++;
        } else {
            add(i, t, 1);
            ji++;
        }
    }
    int ans1 = dinic();

    ou -= ans1;
    ji -= ans1;
    int ans2 = ou / 2 * 2 + ji / 2 * 2;

    ou %= 2;
    ji %= 2;
    cout << ans1 + ans2 + 3 * (ou || ji);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值