1. 程式人生 > 其它 >AtCoder-ARC080D Prime Flip——差分+哥德巴赫猜想

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;
}