[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 @   Troverld  阅读(72)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示