ZYB's Prime
Accepts: 5
Submissions: 89
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
问题描述
ZYB(ZJ−267)在NOIPAK又创造出了一道题:给出N 个数,现在要求将它们分成K 组(K≥1),每组数的个数都≥3,将每组中的数排成一个环,要求相邻的两个数加起来是个质数.ZYB想要问你对于这N个数,能不能将它们分组?
输入描述
第一行一个整数T表示数据组数。 接下来每组数据: 第一行一个正整数N. 第二行N个正整数Ai,描述这N个数. 1≤T≤50,1≤N≤200,1≤Ai≤200,对60%的数据N≤20.
输出描述
T行每行输出YES或NO.
输入样例
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操作处理之后。
实际上,奇数的个数还是要和偶数个数保持相同。
因为偶数个数是不变的。所以,事实上只需要对一个值跑网络流即可。
啦啦啦啦啦~
*/