ARC 080 F Prime Flip - 网络流

首先长度为1的区间可以三次翻转,3+5-7。
长度为2的区间可以二次翻转,5-3。
长度为奇质数的区间可以一次翻转。
长度为4的区间可以二次翻转,7-3。
长度>=6并且是偶数的区间可以根据歌德巴赫猜想拆成两个奇质数的和。
长度>=9并且是奇合数的区间可以根据原歌德巴赫猜想拆成三个奇质数的和。
综上可以归纳:
1,长度是奇质数的区间可以一次翻转;
2,长度是偶数的区间可以两次翻转;
3,否则就要三次翻转;
首先区间操作的话就把原序列差分(异或意义上的),然后每次区间操作相当与是同时翻转两个数字的状态。显然你只会操作两个1。
我们假设1这种情况进行了k次。当k固定的时候,显然按照奇偶性把剩下的数字分成两组,然后尽量同一组内连(第二种情况),最后第三种最多连1次。
由此列个式子就能观察出,显然让k越大越好,因此建图发现是二分图做匹配即可。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<climits>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define build_edge(u,v,f) add_edge(u,v,f),add_edge(v,u,0)
#define notp b
#define N 210
#define M 200010
#define MXV 10000010
#define lint long long
#define INF (INT_MAX/10-10)
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
struct edges{
    int to,pre,resf;
}e[M];int h[N],cur[N],etop,lev[N],a[N];
queue<int> q;int b[MXV],p[MXV];
inline int prelude(int n)
{
    notp[1]=1;
    for(int i=2,c=0;i<=n;i++)
    {
        if(!notp[i]) p[++c]=i;
        for(int j=1;j<=c&&(lint)p[j]*i<=n;j++)
        { notp[i*p[j]]=1;if(i%p[j]==0) break; }
    }
    return 0;
}
inline int bfs(int s,int t)
{
    while(!q.empty()) q.pop();
    memset(lev,0,sizeof(int)*(t+1));
    q.push(s),lev[s]=1;
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=h[x],y;i;i=e[i].pre)
            if(e[i].resf&&!lev[y=e[i].to])
                lev[y]=lev[x]+1,q.push(y);
    }
    return lev[t];
}
int dfs(int s,int t,int a)
{
    if(s==t||!a) return a;int flow=0,f;
    for(int &i=cur[s];i;i=e[i].pre)
        if(lev[e[i].to]==lev[s]+1&&(f=dfs(e[i].to,t,min(a,e[i].resf)))>0)
        {
            e[i].resf-=f,e[((i-1)^1)+1].resf+=f;
            a-=f,flow+=f;if(!a) break;
        }
    return flow;
}
inline int gabs(int x) { return x<0?-x:x; }
inline int isjp(int x) { return (!notp[x])&&(x&1); }
inline int add_edge(int u,int v,int f)
{ return e[++etop].to=v,e[etop].resf=f,e[etop].pre=h[u],h[u]=etop; }
inline int calc(int f,int a,int b) { return a-=f,b-=f,f+a/2*2+b/2*2+(a%2&&b%2)*3; }
inline int dinic(int s,int t,int flow=0)
{ while(bfs(s,t)) memcpy(cur,h,sizeof(int)*(t+1)),flow+=dfs(s,t,INF);return flow; }
int main()
{
    int n,c=0,s,t,sc=0,tc=0;scanf("%d",&n);
    for(int i=1,x;i<=n;i++)
        scanf("%d",&x),b[x]^=1,b[x+1]^=1;
    for(int i=1;i<MXV;i++) if(b[i]) a[++c]=i;
    for(int i=1;i<=c;i++) b[a[i]]=0;
    prelude(a[c]),s=c+1,t=c+2;
    for(int i=1;i<=c;i++)
        if(a[i]&1) build_edge(s,i,1),sc++;
        else build_edge(i,t,1),tc++;
    rep(i,1,c) if(a[i]&1) rep(j,1,c)
        if(isjp(gabs(a[i]-a[j]))) build_edge(i,j,1);
    return !printf("%d\n",calc(dinic(s,t),sc,tc));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值