P5771

发布时间 2023-11-12 13:11:11作者: mRXxy0o0

\(n^3\) 匈牙利最优解,根本叉不掉。

分析

观察序列,如果把两个和为质数的点连一条边,那么原问题就转化成了求最大独立集。

有一种直觉告诉我们这应该是一个二分图,就考虑证明一下。

首先,偶数个 \(1\) 就违背了这一点,但当去掉重复的 \(1\) 后,它就是一个二分图。

不妨反证,设有一个由 \(x,y,z(x\le y\le z,1<y\le z)\) 构成的奇环,则:

\[\begin{cases} x+y\equiv 1 \pmod 2\\ y+z\equiv 1 \pmod 2\\ x+z\equiv 1 \pmod 2 \end{cases} \]

这是矛盾的,所以这个图是二分图。

在二分图上匈牙利求 \(n-\text{最大匹配数}\) 即可。

时间复杂度比较玄学,因为根本跑不满 \(n^3\)

交一发结果发现 T 了一个点,考虑卡常。

卡常

优化枚举

首先可以把枚举所有的点 DFS 一遍改成只枚举某个部的点。在这道题中,数组元素的奇偶性天然分成了两个部,直接判断即可。

经过试验,偶数点较少一些,所以枚举偶数点。

优化建边

根据上述,只连偶数到奇数的单向边即可。

此外,还需要提高缓存命中。故排序时把偶数都放在前面,奇数放后面,也可以方便去掉重复的 \(1\)

由于这是一个稠密图,vector 建边比链式前向星快不少。

优化匈牙利

DFS 常数巨大,改用 BFS 版,原理类似。

其他

比如位运算代替取模,多次定义改成赋值等等。都加上去就行。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3010,M=2e5+10;
int n,a[N],p[M],link[N],ans,fg[N],tim=1,q[N],hh,tt,px[N],py[N],pre[N];
bool vis[M],vx[N],vy[N];
vector<int>G[N];
inline bool cmp(int x,int y){
	return ((x&1)^(y&1))?(~x&1):x>y;
}
inline void init(){
	vis[1]=1;
	for(int i=2;i<M;++i){
		if(!vis[i]) p[++p[0]]=i;
		for(int j=1;j<=p[0]&&1ll*i*p[j]<M;++j){
			vis[i*p[j]]=1;
			if(i%p[j]==0) break;
		}
	}
}
void aug(int v){
	int t;
    while(v){
        t=px[pre[v]];
        px[pre[v]]=v;
        py[v]=pre[v];
        v=t;
    }
}
bool bfs(int s){
    memset(pre,0,sizeof(pre));
    memset(vx,0,sizeof(vx));
    memset(vy,0,sizeof(vy));
    q[hh=tt=1]=s;
    int u;
    while(hh<=tt){
        u=q[hh++];
        vx[u]=1;
        for(int v:G[u]) if(!vy[v]){
            vy[v]=1;
            pre[v]=u;
            if(!py[v]) return aug(v),1;
            q[++tt]=py[v];
        }
    }
    return 0;
}
int main(){
	init();
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	sort(a+1,a+1+n,cmp);
	while(n>1&&a[n-1]==1) --n;
	for(int i=1;i<=n&&(~a[i]&1);++i){
		for(int j=n;j>=1&&(a[j]&1);--j){
			if(!vis[a[i]+a[j]]) G[i].emplace_back(j);
		}
	}
	for(int i=1;i<=n&&(~a[i]&1);++i,++tim) ans+=bfs(i);
	printf("%d",n-ans);
	return 0;
}