AtCoder-ARC080D Prime Flip——差分+哥德巴赫猜想

题目

AtCoder - arc080_d 

题解

很巧妙的一道题。

直接操作显然不好做,把数组差分一下,变为只有每一段连续朝上的开头一个和尾部的后一个为1,其余位置为0,每次操作区间 [l,r] (要求 r-l 为奇素数)把 l 处和 r 处的0/1翻转,要求最后全部变为零。

容易发现差分后1的数量一定是偶数,所以一定是两两翻转消去的,

我们简单地对 r-l 分类讨论一下,有三种情况:

  1. r-l 为奇素数,操作1次;
  2. r-l 为偶数,这时候怎么办呢?看到奇素数我们会想到小学三年级学过的哥德巴赫猜想:任何一个大于4的偶数都可以表示为两个奇素数相加,如6=3+3,8=3=5。虽然是个猜想,但是至少在1e7以内是正确的。另外对于小于等于4的偶数2、4,2=5-3,4=7-3,所以此时操作仅需2次。
  3. r-l 为奇非素数,由于1=7-3-3,而除1以外的所有奇非素数-3过后都是大于4的偶数,所以需要操作3次。

然后我们需要把1两两匹配起来,可以先把1的位置按奇偶性分开,形成一个二分图,差为奇素数的连边,跑一次二分图匹配(每条边贡献1),剩下的点按奇-奇、偶-偶两两匹配删去,贡献2,如果还有剩的,一定是一奇一偶且差为奇非素数,再算一个贡献3。

用匈牙利+朴素判断质数是O(n^3+n^2\sqrt X),用欧拉筛是O(n^3+X)

如果用DINIC跑匹配可以更快,总复杂度大概是O(n^2\sqrt X)O(X)

还可以把\sqrt X内的素数筛出然后用朴素判断,复杂度O(\frac{n^2\sqrt X}{\ln \sqrt X})

如果你还想更快,可以用Miller–Rabin算法,但是没必要啊,过了就行了

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define ll long long
#define MAXN 100005
#define MAXX 10000000
#define INF 0x3f3f3f3f
using namespace std;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
	return f?x:-x;
}
int n,m,a[205],ans;
bool nop[MAXN];
vector<int>pr;
inline int ads(int x){return x>0?x:-x;}
inline void getprime(int n){
	nop[0]=nop[1]=1;
	for(int a=2;a<=n;a++){
		if(!nop[a])pr.push_back(a);
		for(int i=0;i<pr.size()&&pr[i]*a<=n;i++){
			nop[pr[i]*a]=1;
			if(a%pr[i]==0)break;
		}
	}
}
inline bool isprime(int x){
	if(x<MAXN-4)return !nop[x];
	for(int i=0;i<pr.size()&&pr[i]*pr[i]<=x;i++)
		if(x%pr[i]==0)return 0;
	return 1;
}

int f[100005],cur[505],c,d,s1[205],s2[205],IN;
int dt[505];
struct edge{
	int v,id;edge(){}
	edge(int V,int I){v=V,id=I;}
};
vector<edge>G[505];
queue<int>q;
inline void addedge(int u,int v,int w){
	f[IN]=w,f[IN^1]=0;
	G[u].push_back(edge(v,IN)),G[v].push_back(edge(u,IN^1));
	IN+=2;
}
inline bool bfs(int S,int T){
	while(!q.empty())q.pop();
	memset(dt,-1,sizeof(dt));
	dt[S]=0,q.push(S);
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<G[u].size();i++)
			if(f[G[u][i].id]>0&&dt[G[u][i].v]<0)
				dt[G[u][i].v]=dt[u]+1,q.push(G[u][i].v);
	}
	return dt[T]>=0;
}
inline int dfs(int x,int lim,int T){
	if(x==T)return lim;
	int res=lim;
	for(int i=cur[x];i<G[x].size()&&res>0;i++){
		cur[x]=i;
		int v=G[x][i].v,a=G[x][i].id;
		if(f[a]>0&&dt[v]==dt[x]+1){
			int ad=dfs(v,min(res,f[a]),T);
			f[a]-=ad,f[a^1]+=ad,res-=ad;
		}
	}
	return lim-res;
}
inline int dinic(int S,int T){
	int res=0;
	while(bfs(S,T)){
		memset(cur,0,sizeof(cur));
		while(int ad=dfs(S,INF,T))res+=ad;
	}
	return res;
}

signed main()
{
	n=read();
	getprime(MAXN-5);
	for(int i=1,ls=-1;i<=n;i++){
		int x=read();
		if(x>ls+1)a[++m]=x,a[++m]=x+1;
		else a[m]++;
		ls=x;
	}
	for(int i=1;i<=m;i++){
		if(a[i]&1)s1[++c]=a[i];
		else s2[++d]=a[i];
	}
	for(int i=1;i<=c;i++)addedge(0,i,1);
	for(int i=1;i<=d;i++)addedge(c+i,c+d+1,1);
	for(int i=1;i<=c;i++)
		for(int j=1,s;j<=d;j++){
			s=ads(s1[i]-s2[j]);
			if((s&1)&&isprime(s))addedge(i,c+j,1);
		}
	int p=ans=dinic(0,c+d+1);
	ans+=c+d-p-p;
	if((c-p&1)&&(d-p&1))ans++;
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43960287/article/details/113838253