题目大意
给出一个长度为nnn的序列aia_iai,要求删去若干个数,使得剩下的数中任意两个数都不是质数。在满足条件的情况下最多能保留几个数。
有TTT组数据。
1≤T≤4,1≤n≤750,1≤ai≤1091\leq T\leq 4,1\leq n\leq 750,1\leq a_i\leq 10^91≤T≤4,1≤n≤750,1≤ai≤109
时间限制2000ms2000ms2000ms,空间限制512MB512MB512MB
题解
首先,如果一个数kkk不是质数,那一定kkk存在一个质因数ppp满足p≤kp\leq \sqrt kp≤k。
那么,我们可以预处理出5×1045\times 10^45×104以内的所有质数,用这些质数即可判断2×1092\times 10^92×109以内的每个数是不是质数。若枚举每两个数来判断是不是质数,则时间复杂度是O(n2P)O(n^2P)O(n2P)的(其中PPP为5×1045\times 10^45×104以内的质数个数,P=5133P=5133P=5133)。
我们考虑优化。对于每个质数ppp,令bi=ai%pb_i=a_i\% pbi=ai%p,然后将bib_ibi排序,那么此时bib_ibi由若干个值相同的部分组成。假设有一部分的值都为xxx,则我们需要找到值都为p−xp-xp−x的部分,则枚举这两部分的aaa值。设第一部分枚举到aia_iai,第二部分枚举到aja_jaj,则如果ai+aj>pa_i+a_j>pai+aj>p,则ai+aja_i+a_jai+aj一定是ppp的若干倍,那么其一定是合数,做一个标记。
你可能会问:对于每个质数,最多有可能标记n2n^2n2次,那时间复杂度不还是O(n2P)O(n^2P)O(n2P)吗?
对于每个ai+aja_i+a_jai+aj,它只会被它的质因数打标记,而ai+aja_i+a_jai+aj的质因数最多为log(ai+aj)\log(a_i+a_j)log(ai+aj)个,所以平摊下来,时间复杂度为O(n2logv)O(n^2\log v)O(n2logv),其中vvv为ai+aja_i+a_jai+aj的最大值。枚举的时间复杂度为O(nP)O(nP)O(nP),所以这部分的时间复杂度为O(n2logv+nP)O(n^2\log v+nP)O(n2logv+nP)。
当然,也可以用用Miller-Rabin\text{Miller-Rabin}Miller-Rabin算法来判断ai+aja_i+a_jai+aj是不是质数。
知道了每两个数之和是否为质数之后,我们建一个图,对于和为质数的两个数,我们将这两个数在图上对应的点连一条边,那么题意就是求最大点独立集。
我们观察图的性质。
如果组成的质数为偶质数,那么这个质数一定为222,组成它的两个aaa值一定都为111。也就是说,在所有aia_iai中只保留最多一个111,即可保证不会出现偶质数。这个在输入的时候特殊处理一下即可。
那么,ai+aja_i+a_jai+aj为质数的话,只可能为奇质数,那么aia_iai和aja_jaj的奇偶性一定不同。于是,上面的图就是一个二分图了。二分图的最大点独立集等于二分图的顶点数减去二分图的最大匹配数,那我们只需要求这个二分图的最大匹配即可。
这个二分图的点数为nnn,边数为m=n2m=n^2m=n2。我们可以用匈牙利算法来求二分图的最大匹配,时间复杂度为O(nm)O(nm)O(nm),即O(n3)O(n^3)O(n3)。感觉会TLE\text{TLE}TLE,但是匈牙利的时间复杂度为点数乘边数。设左边的点数为xxx,则最坏情况下要做的次数为n⋅x(n−x)n\cdot x(n-x)n⋅x(n−x),最大值为n34\dfrac{n^3}{4}4n3,而这是跑不满的。所以用匈牙利算法是可行的。
当然,也可以用Dinic\text{Dinic}Dinic算法来求二分图的最大匹配,时间复杂度为O(nm)O(n\sqrt m)O(nm),即O(n2)O(n^2)O(n2)。
求完最大匹配之后,用点数减去最大匹配即可得到这个图的最大点独立集,也就是答案。
时间复杂度为O(∑(n2logv+nP+n3))O(\sum(n^2\log v+nP+n^3))O(∑(n2logv+nP+n3)),其中n3n^3n3是带一个14\dfrac 1441的常数的,而且这跑不满,时限还给了2000ms2000ms2000ms,所以这是可以过的。
code
#include<bits/stdc++.h>
using namespace std;
const int N=750,P=50000;
int T,n,p1,p2,q1,q2,ans,a[N+5],p[P+5],z[P+5],gt[N+5],t[N+5][N+5];
int tot,d[2*N*N+5],l[2*N*N+5],r[N+5],cx[N+5],cy[N+5];
struct node{
int x,id;
}b[N+5];
bool cmp(node ax,node bx){
return ax.x<bx.x;
}
void add(int xx,int yy){
l[++tot]=r[xx];d[tot]=yy;r[xx]=tot;
}
void init(){
for(int i=2;i<=P;i++){
if(!z[i]) p[++p[0]]=i;
for(int j=1;j<=p[0]&&i*p[j]<=P;j++){
z[i*p[j]]=1;
if(i%p[j]==0) break;
}
}
}
int dfs(int u){
for(int i=r[u];i;i=l[i]){
if(gt[d[i]]) continue;
gt[d[i]]=1;
if(!cy[d[i]]||dfs(cy[d[i]])){
cx[u]=d[i];cy[d[i]]=u;
return 1;
}
}
return 0;
}
int main()
{
// freopen("cooking.in","r",stdin);
// freopen("cooking.out","w",stdout);
init();
scanf("%d",&T);
while(T--){
scanf("%d",&n);
tot=0;
memset(r,0,sizeof(r));
memset(t,0,sizeof(t));
for(int i=1,bz=0;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]==1){
if(!bz) bz=1;
else{
--i;--n;continue;
}
}
t[i][i]=1;
}
for(int nw=1;nw<=p[0];nw++){
for(int i=1;i<=n;i++) b[i]=(node){a[i]%p[nw],i};
sort(b+1,b+n+1,cmp);
p1=p2=1;q1=q2=n;
for(;p2<=n&&b[p2].x==0;p2++){
for(int i=p1;i<=p2;i++){
int w1=b[i].id,w2=b[p2].id;
t[w1][w2]=t[w2][w1]=1;
}
}
--p2;p1=p2+1;
for(;;p1=p2+1,q1=q2-1){
while(p1<=n&&q1>=1&&b[p1].x+b[q1].x!=p[nw]){
if(b[p1].x+b[q1].x<p[nw]) ++p1;
else --q1;
}
if(p1>n||q1<1||p1>q1) break;
p2=p1;q2=q1;
while(p2+1<=n&&b[p2+1].x==b[p1].x) ++p2;
while(q2-1>=1&&b[q2-1].x==b[q1].x) --q2;
for(int i=p1;i<=p2;i++){
for(int j=q2;j<=q1;j++){
int w1=b[i].id,w2=b[j].id;
if(a[w1]+a[w2]>p[nw]) t[w1][w2]=t[w2][w1]=1;
}
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(!t[i][j]) add(i,j);
}
}
ans=n;
memset(cx,0,sizeof(cx));
memset(cy,0,sizeof(cy));
for(int i=1;i<=n;i++){
if(a[i]%2==0) continue;
if(!cx[i]){
memset(gt,0,sizeof(gt));
ans-=dfs(i);
}
}
printf("%d\n",ans);
}
return 0;
}