【国集作业】【ARC080F】Prime Flip【差分】【二分图】

小视野链接
洛谷链接

题目大意

有无限个硬币,其中第 x1,x2,xn x 1 , x 2 … , x n 反面朝上,其余正面朝上。每次选一个奇质数 p p 和一个合适的位置,并把这个位置后连续p个硬币翻转,求最小操作数。

题解

这种改变一个区间的状态的题目一般套路就是差分,这样能把区间操作转化为点对操作。比如这一题差分 b[i]=a[i]a[i1] b [ i ] = a [ i ] ⨂ a [ i − 1 ] 表示异或, a[i] a [ i ] 表示当前硬币状态)。那么现在的操作将第 b[i] b [ i ] 个位置和第 b[i+p] b [ i + p ] 个位置取反,求所有 b[i] b [ i ] 变为 0 0 的最小操作数。
现在若要改变b[i] b[j] b [ j ] 的状态,分以下三种情况讨论:

1. |ij| | i − j | 为奇质数:一次操作即可
2. |ij| | i − j | 为偶数:根据哥德巴赫猜想,把这个偶数段分成两个奇质数段分别翻转即可(特别地,当 |ij|=4 | i − j | = 4 时并不满足此猜想,这时把4分成两个奇质数之差(如7-5)即可)。操作数为2
3. |ij| | i − j | 为奇合数:可以把它分成三个奇质数之和即可(至于为什么一定可行请读者自行思考…我太懒了。。。。)

这时就有一种贪心的思想:优先选第一种情况。我们可以用二分图来使情况1被使用得最多。把 b[i]=1 b [ i ] = 1 的位置按 i i 的奇偶性分组,连边Li>Ri+p p p 为奇质数且b[i]=b[i+p]=1)。跑出来的最大匹配就是选择方案一最多的方案。剩下奇偶性相同的位置使用第二种方案,最后如果还有剩余就只能采用第三种方案。

代码

我用了dinic跑二分图。。。结果更慢。。。

#pragma GCC optimize(3)
#include<iostream>
#include<iomanip>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<set> 
#include<map>
#include<string>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<ctime>
#define ll long long
#define db double
#define inf 10000001
#define infm 50001
#define INF (int)1e8 
#define mod (int)(1e9+7)
#define pi acos(-1)
#define rd(n) {n=0;char ch;int f=0;do{ch=getchar();if(ch=='-'){f=1;}}while(ch<'0'||ch>'9');while('0'<=ch&&ch<='9'){n=(n<<1)+(n<<3)+ch-48;ch=getchar();}if(f)n=-n;}
using namespace std;

int p[inf],ckp[inf],pcnt;
const int lim=10000000;

void pre_work(void){
    ckp[0]=ckp[1]=1;
    for (int i=2;i<=lim;i++){
        if (!ckp[i]){
            p[++pcnt]=i;
        }
        for (int j=1;j<=pcnt && p[j]*i<=lim;j++){
            ckp[p[j]*i]=1;
            if (i%p[j]==0){
                break;
            }
        }
    }
    return;
}

int S,T;

struct edge{
    int nxt,y,s;
}e[infm*2];
int ecnt=1,head[infm],h[infm];

void addedge(int x,int y,int s){
    ecnt++;
    e[ecnt].nxt=head[x];
    e[ecnt].y=y;
    e[ecnt].s=s;
    head[x]=ecnt;
    return;
}

void ae(int x,int y,int s){
    addedge(x,y,s);
    addedge(y,x,0);
    return;
}

queue <int> q;
int dep[inf];

int dfs(int u,int flow){
    if (!flow || u==T){
        return flow;
    }
    int ans=0;
    for (int i=h[u];i;i=e[i].nxt){
        h[u]=i;
        int v=e[i].y,s=e[i].s,now;
        if (dep[v]!=dep[u]+1){
            continue;
        }
        now=dfs(v,min(flow,s));
        if (now){
            ans+=now;
            flow-=now;
            e[i].s-=now;
            e[i^1].s+=now;
            if (!flow){
                break;
            }
        }
    }
    return ans;
}

int dinic(void){
    int ans=0;
    while (1){
        while (!q.empty()){
            q.pop();
        }
        memcpy(h,head,sizeof(head));
        memset(dep,-1,sizeof(dep));
        dep[S]=0;
        q.push(S);
        while (!q.empty()){
            int u=q.front(),v,s;
            q.pop();
            for (int i=head[u];i;i=e[i].nxt){
                v=e[i].y,s=e[i].s;
                if (dep[v]<0 && s){
                    dep[v]=dep[u]+1;
                    q.push(v);
                }
            }
        }
        if (dep[T]<0){
            break;
        }
        ans+=dfs(S,INF);
    }
    return ans;
}

int n;
int a[inf];
int L[inf],R[inf];

int main(){
    pre_work();
    rd(n)
    int x,mx=0;
    for (int i=1;i<=n;i++){
        rd(x)
        a[x]++;
        mx=max(mx,x);
    }
    int cntl=0,cntr=0;
    for (int i=1;i<=mx+1;i++){
        if (a[i]!=a[i-1]){
            if (i&1){
                L[++cntl]=i;
            }
            else{
                R[++cntr]=i;
            }
        }
    }
    S=0,T=cntl+cntr+1;
    for (int i=1;i<=cntl;i++){
        ae(S,i,1);
    }
    for (int i=1;i<=cntr;i++){
        ae(i+cntl,T,1);
    }
    for (int i=1;i<=cntl;i++){
        for (int j=1;j<=cntr;j++){
            if (!ckp[abs(L[i]-R[j])]){
                ae(i,j+cntl,1);
            }
        }
    }
    int mxf=dinic();
    int ans=mxf;
    ans+=(cntl-mxf)/2*2+(cntr-mxf)/2*2;
    ans+=((cntl-mxf)&1)*3;
    printf("%d\n",ans);
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值