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

  • 相关阅读:
    C# 数据库连接字符串拼接
    C# 线程同步计数存在的问题
    字符串操作
    字符串位置
    6个基本函数
    占位符
    str转换成int
    python运算符6
    python运算符5
    python运算符4
  • 原文地址:https://www.cnblogs.com/Troverld/p/14610777.html
Copyright © 2020-2023  润新知