peiwenjun's blog 没有知识的荒原

ARC080D Prime Flip 题解

题目描述

有无限枚硬币,其中恰有 \(n\) 枚硬币初始正面朝上,位置为 \(x_1,\cdots,x_n\)

你每次可以选择一段区间 \([l,r]\) ,要求 \(r-l+1\) 是奇质数,然后翻转区间中的所有硬币。

求最少的操作次数,使得所有硬币都背面朝上。

数据范围

  • \(1\le n\le 100,1\le x_1\lt\cdots\lt x_n\le 10^7\)

时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{256MB}\)

分析

记奇质数集合为 \(\text{prime}\)

显然需要差分,那么每次操作等价于选择 \(p\in\text{prime}\) ,然后给 \(a_l\)\(a_{l+p}\) 都异或上\(1\)

记差分数组为 \(b_i\) ,显然 \(b\) 中有偶数个 \(1\) ,并且个数 \(\le 2n\)

考虑将这些点两两匹配消去。

假设现在要消两个距离为 \(d\) 的点,那么需要用若干个 \(\text{prime}\) 中的数通过加减凑出 \(d\)

  • \(d\in\text{prime}\) :显然代价为 \(1\)
  • \(2\mid d\) :代价为 \(2\) 。这是因为哥德巴赫猜想在 \(10^7\) 范围内成立,注意可以使用减法。
  • \(else\) :代价为 \(3\)

这是一个完全图最小权完美匹配问题,由于不是二分图,所以似乎无从下手。

但事实上,根据边权的特殊性质,我们可以贪心地连尽量多权值为 \(1\) 的边(由于每条权值为 \(1\) 的边两端奇偶性不同,所以可以二分图最大匹配解决),然后用 \(2\) 的代价贪心匹配奇偶性相同的点,最后用不超过 \(1\) 条权值为 \(3\) 的边解决剩下的点即可。

时间复杂度 \(\mathcal O(n^{2.5})\)

#include<bits/stdc++.h>
using namespace std;
const int maxn=205,maxm=3e4,maxv=1e7+5,inf=1e9;
int n,s,t,x,k1,k2,res,tot=1;
int a[maxv],c[2][maxn];
int b[maxv],p[maxv];
int head[maxn],to[maxm],f[maxm],nxt[maxm];
int d[maxn];
void init(int n)
{
    for(int i=2,cnt=0;i<=n;i++)
    {
        if(!b[i]) p[++cnt]=i;
        for(int j=1;j<=cnt&&i*p[j]<=n;j++)
        {
            b[i*p[j]]=1;
            if(i%p[j]==0) break;
        }
    }
    b[1]=b[2]=1;
}
void addedge(int u,int v,int w)
{
    nxt[++tot]=head[u],to[tot]=v,f[tot]=w,head[u]=tot;
    nxt[++tot]=head[v],to[tot]=u,f[tot]=0,head[v]=tot;
}
bool bfs()
{
    queue<int> q;
    memset(d,0,sizeof(d));
    d[s]=1,q.push(s);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(f[i]&&!d[v])
            {
                d[v]=d[u]+1,q.push(v);
                if(v==t) return true;
            }
        }
    }
    return false;
}
int dfs(int u,int limit)
{
    if(u==t) return limit;
    int flow=0;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(!f[i]||d[v]!=d[u]+1) continue;
        int k=dfs(v,min(f[i],limit-flow));
        if(!k) d[v]=0;
        f[i]-=k,f[i^1]+=k,flow+=k;
        if(flow==limit) break;
    }
    return flow;
}
int dinic()
{
    int flow=0;
    while(bfs()) flow+=dfs(s,inf);
    return flow;
}
int main()
{
    scanf("%d",&n),init(maxv-5);
    for(int i=1;i<=n;i++) scanf("%d",&x),a[x]^=1,a[x+1]^=1;
    for(int i=1;i<maxv;i++)
    {
        if(!a[i]) continue;
        if(i&1) c[0][++k1]=i;
        else c[1][++k2]=i;
    }
    s=0,t=k1+k2+1;
    for(int i=1;i<=k1;i++) addedge(s,i,1);
    for(int i=1;i<=k2;i++) addedge(k1+i,t,1);
    for(int i=1;i<=k1;i++)
        for(int j=1;j<=k2;j++)
            if(!b[abs(c[0][i]-c[1][j])])
                addedge(i,k1+j,1);
    res=dinic(),k1-=res,k2-=res;
    res+=(k1/2+k2/2)*2,k1%=2,k2%=2;
    if(k1&&k2) res+=3;
    printf("%d\n",res);
    return 0;
}

posted on 2022-08-20 16:27  peiwenjun  阅读(8)  评论(0)    收藏  举报

导航