ARC080 F

来源:互联网 发布:服务器 定时关机 软件 编辑:程序博客网 时间:2024/06/05 00:55

发现我们每次区间取反,相邻位置的正反关系只有两个位置发生改变
我们定义bi为ai和ai-1的正反关系,即ai=ai-1时bi=0,否则bi=1,每次取反l~r,b[l]和b[r+1]会发生改变
容易发现b[i]=1的位置一定是偶数个,我们将他们取出来
因为每次取反一定会改变两个b[i],所以我们将这些位置两两配对消去
两个位置i,j,有三种配对
|i-j|是奇素数,可以直接消去,最少花费1次操作
|i-j|是偶数,可以由奇素数的和(哥德巴赫猜想?)或差得到,最少花费2次
|i-j|是奇非素数,由奇素数和偶数差得到,最少花费3次
将b[i]=1的i按奇偶性分为两个集合
不同集合之间的配对是第1、3种配对
同一集合间的配对是第2种
可以做第一种配对的i,j之间连边,找二分图最大匹配
剩下的两个集合内部两两第二种配对
如果还余1个,作第三种配对

code:

#include<set>#include<map>#include<deque>#include<queue>#include<stack>#include<cmath>#include<ctime>#include<bitset>#include<string>#include<vector>#include<cstdio>#include<cstdlib>#include<cstring>#include<climits>#include<complex>#include<iostream>#include<algorithm>#define ll long longusing namespace std;const int maxn = 1e7+10;const int maxm = 210;int p[4100000],pri;bool v[maxn];void pre(){    for(int i=2;i<maxn;i++)    {        if(!v[i]) p[++pri]=i;        for(int j=1,k=i*p[j];j<=pri;j++,k=i*p[j])        {            if(k>=maxn) break;            v[k]=true;            if(i%p[j]==0) break;        }    }v[1]=v[2]=true;}int n,m;int a[maxm],b[maxm];bool s[maxn];int p1[maxm],p2[maxm];int p1n,p2n;bool mp[maxm][maxm],ev[maxm];int match[maxm],bel[maxm];bool Find(const int x){    if(ev[x]) return false;    ev[x]=true;    for(int i=1;i<=p2n;i++) if(mp[x][i])    {        if(!bel[i]||Find(bel[i])) { bel[i]=x; return true; }    }    return false;}int main(){    pre();    scanf("%d",&n);    for(int i=1;i<=n;i++) scanf("%d",&a[i]),s[a[i]]=true;    for(int i=1;i<maxn;i++)        if(s[i]!=s[i-1]) b[++m]=i;    for(int i=1;i<=m;i++)    {        if(b[i]&1) p1[++p1n]=b[i];        else p2[++p2n]=b[i];    }    for(int i=1;i<=p1n;i++)    {        for(int j=1;j<=p2n;j++) if(!v[abs(p1[i]-p2[j])]) mp[i][j]=true;    }    int k=0;    for(int i=1;i<=p1n;i++) if(!match[i])    {        for(int j=1;j<=p1n;j++) ev[j]=false;        if(Find(i)) k++;    }    p1n-=k; p2n-=k;    int re=k;    re+=p1n/2*2+p2n/2*2;    re+=p1n%2*3;    printf("%d\n",re);    return 0;}
原创粉丝点击