【AtCoder】【ARC080F】Prime Flip

Description

有无限多的硬币排成一排,其中有一些位置的的硬币是反面的,其他的都是正面的,
现在你每次可以翻转一个区间的所有硬币,要求区间的长度必须为奇质数,
求最少操作次数,

Solution

先把原序列差分,每次就只会更改两个点,
分以下的几种情况讨论:
1. 区间长度为奇质数,代价为1;
2. 区间长度为偶数,代价为2(2:5-3, 4:7-3,大于6的用哥德巴赫猜想);
3. 区间长度为非质数奇数,代价为3(随便一个偶数-3),

以偶了以上的结论,贪心的想,尽量把要做的点两两匹配成奇质数,剩下的用偶数匹配,最后实在不行就补一个奇数的,

匹配用匈牙利即可

Code

#include <cstdio>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define min(q,w) ((q)>(w)?(w):(q))
#define max(q,w) ((q)<(w)?(w):(q))
using namespace std;
const int N=350,M=1e7;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n,ans;
bool az[M+10],prz[M+10];
bool B[N][N];
int pr[M/5];
int a[N],b[N],zx[N];
int z[N],TI;
void Pre()
{
    fo(i,2,M)
    {
        if(!prz[i])pr[++pr[0]]=i;
        fo(j,1,pr[0])
        {
            int t=i*pr[j];
            if(t>M)break;
            prz[t]=1;
            if(!t%pr[j])break;
        }
    }
    prz[1]=prz[2]=1;
}
bool OK(int q)
{
    if(z[q]==TI)return 0;
    z[q]=TI;
    fo(i,1,b[0])if(B[q][i]&&(!zx[i]||OK(zx[i])))return zx[i]=q,1;
    return 0;
}
int main()
{
    int q;
    Pre();
    read(n);
    fo(i,1,n)az[read(q)]=1;
    fo(i,1,M+1)if(az[i]!=az[i-1])
    {
        if(i&1)a[++a[0]]=i;
        else b[++b[0]]=i;
    }
    fo(i,1,a[0])fo(j,1,b[0])if(!prz[abs(a[i]-b[j])])B[i][j]=1;
    q=0;
    fo(i,1,a[0])
    {
        ++TI;if(OK(i))++q;
    }
    ans=q+2*((a[0]-q)/2+(b[0]-q)/2);
    if((a[0]-q)&1)ans+=3;
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/howarli/article/details/79521172