【BestCoder Round 65E】【网络流+讨论 奇偶分类思想】n个数形成若干至少3元素素数环的可行性检验 数可以为1

ZYB's Prime

 
 Accepts: 5
 
 Submissions: 89
 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 131072/131072 K (Java/Others)
问题描述
ZYB(ZJ-267)ZYB(ZJ267)NOIP AKNOIPAK又创造出了一道题:给出NN 个数,现在要求将它们分成KK 组(K \geq 1K1),每组数的个数都\geq 33,将每组中的数排成一个环,要求相邻的两个数加起来是个质数.ZYBZYB想要问你对于这NN个数,能不能将它们分组?
输入描述
第一行一个整数TT表示数据组数。

接下来每组数据:

  第一行一个正整数NN.

  第二行NN个正整数AiAi,描述这NN个数.

1 \leq T \leq 501T50,1 \leq N \leq 2001N200,1 \leq A_i \leq 2001Ai200,对6060%的数据N \leq 20N20.
输出描述
TT行每行输出YESYESNONO.
输入样例
2
7
3 4 8 9 1 1 1
3
1 2 3
输出样例
YES
NO


#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;}
template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;}
const int N=200+10,M=20400+10,Z=1e9+7,ms63=1061109567;
int casenum,casei;
bool e[400+10];//最大边数为(100*100+200)*2
void prime()
{
	int top=400;
	for(int i=2;i<=top;i++)if(e[i]==0)
	{
		for(int j=i+i;j<=top;j+=i)e[j]=1;
	}
}
int n,id,ST,ED;
int a[N],first[N];
int w[M],c[M],cap[M],nxt[M];
void ins(int x,int y,int cap_)
{
	id++;
	w[id]=y;
	cap[id]=cap_;
	nxt[id]=first[x];
	first[x]=id;

	id++;
	w[id]=x;
	cap[id]=0;
	nxt[id]=first[y];
	first[y]=id;
}
int d[N];
bool bfs()
{
	MS(d,-1);d[ST]=0;
	queue<int>q;q.push(ST);
	while(!q.empty())
	{
		int x=q.front();q.pop();
		for(int z=first[x];z;z=nxt[z])if(cap[z])
		{
			int y=w[z];
			if(d[y]==-1)
			{
				d[y]=d[x]+1;
				if(y==ED)return 1;
				q.push(y);
			}
		}
	}
	return 0;
}
int dfs(int x,int all)
{
	if(x==ED)return all;
	int use=0;
	for(int z=first[x];z;z=nxt[z])if(cap[z])
	{
		int y=w[z];
		if(d[y]==d[x]+1)
		{
			int tmp=dfs(y,min(cap[z],all-use));
			cap[z]-=tmp;
			cap[z^1]+=tmp;
			use+=tmp;
			if(use==all)break;
		}
	}
	if(use==0)d[x]=-1;
	return use;
}
int dinic()
{
	int ans=0;
	while(bfs())ans+=dfs(ST,n*2);
	return ans;
}
bool vis[N];
int b[N][N],sum;
int p[N];
int pp[N];
bool check()
{
	int one=0,odd=0,even=0;
	for(int i=1;i<=n;i++)
	{
		if(a[i]==1)p[++one]=i;
		if(a[i]&1)
		{
			++odd;
			ins(ST,i,2);
			for(int j=1;j<=n;j++)if(a[j]!=1&&!e[a[i]+a[j]])ins(i,j,1);//细节:我们不使得1向1连边。
		}
		else
		{
			++even;
			ins(i,ED,2);
		}
	}
	if(even>odd)return 0;
	if(even==odd)return dinic()==n;
	if(/*even<odd&&*/odd-even<=one)									//这是一定会缩1的情况
	{
		int dec=odd-even;
		for(int z=first[p[one]];z;z=nxt[z])if(cap[z]==1)cap[z]=2;	//把缩的点合并成一个special one
		for(int i=1;i<=dec;++i)first[p[i]]=0;						//把去除的点都delete掉
		if(dec==one&&one<3)return 0;								//如果把1全部删掉,要求1的个数必须至少为3
		else return dinic()==even+even;
	}
	else return 0;
}
int main()
{
	prime();
	scanf("%d",&casenum);
	for(casei=1;casei<=casenum;++casei)
	{
		scanf("%d",&n);
		ST=0;ED=n+1;
		MS(first,0);id=1;
		for(int i=1;i<=n;i++)scanf("%d",&a[i]);
		puts(check()?"YES":"NO");
	}
	return 0;
}
/*
【trick&&吐槽】
这题好可惜啊!
比赛的时候就差一种情况没有考虑到!就是1+x=奇数,恰好它们需要在一起的情况。
这种情况的解法是,我们考虑把1

【题意】
给你n(1<=n<=200)个数,每个数的数值范围都在[1,200]。
我们想要把这些数分为若干组。
使得——
1,每组内数的个数不超过3,
2,每组的数形成一个环,环上两两相邻的数的和都是素数。

【类型】
网络流

【分析】
这道题是 CF290(Div2)E的升级版。
不同的是,这道题的数值范围包括了1.

如果不包括1,问题会怎么样?
显然,素数不可能是2,只会是奇数。
于是,相邻两个数必定是一奇一偶。
于是,每个环中数的个数也必定为偶数个。
(题目中也必须要使得奇数和偶数的个数都相同)

然后,我们把所有奇数向与之之和为素数的偶数之间连一条流量为1的边
超级源点向所有奇数连一条流量为2的边
所有奇数向超级汇点连一条流量为2的边
然后如果最大流==点数n,那么说明有解。

这样建图,每个奇数必然会匹配到2个偶数,
于是自然使得每个组中,数的个数都必然至少为4个,保证了做法天衣无缝。

===============================================================

问题回归,现在数可以为1了。
会产生什么影响呢?
如果只有1个1,是没有任何影响的。
如果超过1个1,两个1之间是可以形成一个素数的,即,会出现奇数向奇数连边的情况。
而且,其实对我们产生干扰的,也只有1向1连边这一种情况。

(情况1)如果这些1不形成环,只是链关系的话,
我们可以考虑移除1~n-1个1,设剩余数的个数为m,我们需要查看,是否剩下的数可以跑出流量为m的最大流。
至于我么移除的1,随便和剩下的哪个1相邻即可。(也就是有一种——1个1可以拆出多个1的意味。)
这个肯定不会把是YES的情况判定为NO。这种做法下,1的个数需要至少为2个。

(情况2)然而,如果1的个数如果是大于等于3,我们是可以把所有的1都移除的。因为这些1可以自成一环。

以上做法的思想是什么呢?
就是因为发现1可以与自己相连。所以采取的一种——
"如果1多了,以至于出现1和1连边的情况了,那么我们试着减少1的数量"的做法。

(情况3)然而,这个量的减少,有时候会产生错误——
因为1的减少,我们可能一个环中只剩下2个数了。
如果有这么的一个情况出现,这种环的个数,也必然只有1个。否则2个环可以拼起来。
于是,我们缩1,不能盲目缩,我们可以缩出special one。

什么叫做special one呢?
就是这个1,连向的sum=prime的偶数的边的流量,由1变成了2。
这个解决了情况3出现的影响。也不会产生任何非法干扰。于是就可以AC掉这题啦!

【时间复杂度&&优化】
这题我会不断删点,不断做网络流。
然而流量是不断减少的,于是我们每次都要重置边的流量。
所以,我们可以使得一开始从流量最少的情况入手,不断加边。这样就不会重置边,可以有加速的奇效。

然而,我们还发现,在经过我们的缩1操作处理之后。
实际上,奇数的个数还是要和偶数个数保持相同。
因为偶数个数是不变的。所以,事实上只需要对一个值跑网络流即可。

啦啦啦啦啦~

*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值