[JSOI2016]反质数序列

IV.[JSOI2016]反质数序列

神题……想了一下午才想出来……

同前几题一样,我们可以提出所有和为质数的对,然后跑二分图最大独立集。

先证明一下它为什么是二分图:我们可以令所有奇数为左部,所有偶数为右部。则所有同部间的对的和都是偶数(奇+奇=偶,偶+偶=偶)。则它是一个二分图。

等等,我们好像没有考虑\(1\)!有\(1+1=2\),它们尽管在同一部,但和却是一个质数。

这里我的想法就比较naive了:首先强制选择一个\(1\),在删去所有与\(1\)的和为质数的数后的图上跑独立集。这种方案的答案为(独立集大小\(+1\))(这个\(1\)是强制选的那个\(1\))。

而第二种做法是强制不选\(1\),这种方案的答案为(独立集大小)。

然后最终答案即为两种方案的\(\max\)

这种naive的想法固然可行,但是,看了题解后,我发现了更简单的做法:对于重复的\(1\),只考虑1次(你不能同时选上两个\(1\))。

代码:

#include<bits/stdc++.h>
using namespace std;
int n,num[3010],one,head[3010],cnt,cur[3010],dep[3010],res,S,T,ans;
bool prime[200100];
bool PRIME(int ip){
	if(ip<2)return false;
	for(int i=2;i*i<=ip;i++)if(!(ip%i))return false;
	return true;
}
struct node{
	int to,next,val;
}edge[4001000];
void ae(int u,int v,int w){
//	printf("(%d,%d)\n",u,v);
	edge[cnt].next=head[u],edge[cnt].to=v,edge[cnt].val=w,head[u]=cnt++;
	edge[cnt].next=head[v],edge[cnt].to=u,edge[cnt].val=0,head[v]=cnt++;
}
queue<int>q;
inline bool bfs(){
	memset(dep,0,sizeof(dep)),q.push(S),dep[S]=1;
	while(!q.empty()){
		register int x=q.front();q.pop();
		for(register int i=cur[x]=head[x];i!=-1;i=edge[i].next)if(edge[i].val&&!dep[edge[i].to])dep[edge[i].to]=dep[x]+1,q.push(edge[i].to);
	}
	return dep[T]>0;
}
bool reach;
inline int dfs(int x,int flow){
	if(x==T){
		res+=flow;
		reach=true;
		return flow;
	}
	int used=0;
	for(register int &i=cur[x];i!=-1;i=edge[i].next){
		if(!edge[i].val||dep[edge[i].to]!=dep[x]+1)continue;
		register int ff=dfs(edge[i].to,min(edge[i].val,flow-used));
		if(ff){
			edge[i].val-=ff;
			edge[i^1].val+=ff;
			used+=ff;
			if(used==flow)break;
		}
	}
	return used;
}
inline void Dinic(){
	while(bfs()){
		reach=true;
		while(reach)reach=false,dfs(S,0x3f3f3f3f);
	}	
}
int main(){
	for(int i=1;i<=200000;i++)prime[i]=PRIME(i);
	scanf("%d",&n),S=n+1,T=n+2;
	for(int i=1;i<=n;i++)scanf("%d",&num[i]),one+=(num[i]==1);
	memset(head,-1,sizeof(head));
	for(int i=1;i<=n;i++){
		if(num[i]==1)continue;
		if(prime[num[i]+1])continue;
		ans++;
		if(num[i]&1)ae(S,i,1);
		else ae(i,T,1);
		for(int j=i+1;j<=n;j++){
			if(num[j]==1)continue;
			if(prime[num[j]+1])continue;
			if(!prime[num[i]+num[j]])continue;
			if(num[i]&1)ae(i,j,1);
			else ae(j,i,1);
		}
	}
	Dinic();
	ans=ans-res;
	if(one)ans++;
	for(int i=1;i<=n;i++){
		if(num[i]==1)continue;
		if(prime[num[i]+1]){
			if(num[i]&1)ae(S,i,1);
			else ae(i,T,1);			
		}
		for(int j=i+1;j<=n;j++){
			if(num[j]==1)continue;
			if(!prime[num[i]+1]&&!prime[num[j]+1])continue;
			if(!prime[num[i]+num[j]])continue;
			if(num[i]&1)ae(i,j,1);
			else ae(j,i,1);
		}
	}
	Dinic();
	ans=max(ans,n-one-res);
	printf("%d\n",ans);
	return 0;
}

posted @ 2021-04-02 15:04  Troverld  阅读(71)  评论(0编辑  收藏  举报