[ARC080D]Prime Flip

这篇博客主要探讨了一种名为PrimeFlip的游戏,玩家需要通过翻转区间来达到特定的目标状态。博主分析了翻转操作的性质,并提出通过二分图最大匹配算法求解最短操作序列。博客中详细解释了如何处理不同长度区间翻转的步数,并给出了O(n^3)复杂度的解决方案。最后,博主提供了源代码实现。
摘要由CSDN通过智能技术生成

Prime Flip

题解

挺简单的

很容易发现,如果要将一个点 i i i翻转的话,需要进行 [ 1 , i ] [1,i] [1,i], [ 1 , i − 1 ] [1,i-1] [1,i1]个区间翻转。
我们可以先将所有的翻转给列出来,消去相同的翻转,很明显剩下的翻转数一定是偶数。
我们可以将翻转两两匹配,将翻转 [ 1 , l − 1 ] [1,l-1] [1,l1] [ 1 , r ] [1,r] [1,r]匹配在一起,变成对区间 [ l , r ] [l,r] [l,r]的翻转。

对于翻转一个区间 [ l , r ] [l,r] [l,r],我们需要分三种情况讨论:

1.若 r − l + 1 r-l+1 rl+1为奇质数,一步即可。
2.若 r − l + 1 r-l+1 rl+1为偶数,根据哥德巴赫猜想,两步即可(额外考虑 2 = 5 − 3 , 4 = 7 − 3 2=5-3,4=7-3 2=53,4=73)。
3.若 r − l + 1 r-l+1 rl+1为奇非质数,减去一个奇质数,变成偶数的情况,3步即可(额外考虑 1 = 7 − 3 − 3 1=7-3-3 1=733)。

由于第1种情况的权值是最小的,我们想尽可能将翻转都匹配为第1种情况。
由于第一种情况同一区间的两个翻转奇偶性一定是不同的,我们可以根据翻转位置的奇偶建二分图,将相差为奇质数的翻转连边,最后再跑二分图最大匹配,看能匹配多少。
对于匹配到的两个翻转,作为第一种情况。匹配不了的再同奇偶之间匹配,作为第2种情况。
最后最多也只会让奇和偶之间各多出一个,作为第3种情况匹配。
由于总的翻转次数为偶数,所以一定可以全部匹配完。

时间复杂度 O ( n 3 ) O\left(n^3\right) O(n3)

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 205
#define MAXM 10000005
#define lowbit(x) (x&-x)
#define reg register
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
const int INF=0x7f7f7f7f;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int n,a[MAXN],b[MAXN],idx,c[MAXN],d[MAXN],cntc,cntd,ans,p[MAXN];
int prime[MAXM/10],cntp,head[MAXN],tot;bool oula[MAXM],vis[MAXN];
struct edge{int to,nxt;}e[MAXN*MAXN];
void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
void init(){
	oula[1]=1;
	for(int i=2;i<=1e7;i++){
		if(!oula[i])prime[++cntp]=i;
		for(int j=1;j<=cntp&&prime[j]*i<=1e7;j++){
			oula[i*prime[j]]=1;
			if(i%prime[j]==0)break;
		}
	}
}
bool misaka(int x){
	vis[x]=1;
	for(int i=head[x];i;i=e[i].nxt)
		if(p[e[i].to]==-1||(!vis[p[e[i].to]]&&misaka(p[e[i].to])))
			return (p[e[i].to]=x,1);
	return 0;
}
int sakura(){
	int res=0;for(int i=1;i<=cntd;i++)p[i]=-1;
	for(int i=1;i<=cntc;i++){
		for(int j=1;j<=cntc;j++)vis[j]=0;
		if(misaka(i))res++;
	}
	return res;
}
signed main(){
	read(n);for(int i=1,x;i<=n;i++)read(x),a[i*2-1]=x-1,a[i*2]=x;
	init();sort(a+1,a+2*n+1);
	for(int i=1;i<=2*n;i++){if(a[i]==a[i+1]){i++;continue;}b[++idx]=a[i];}
	for(int i=1;i<=idx;i++)((b[i]&1)?c[++cntc]:d[++cntd])=b[i];
	for(int i=1;i<=cntc;i++)
		for(int j=1;j<=cntd;j++)
			if(!oula[Fabs(c[i]-d[j])])addEdge(i,j);
	int tmp=sakura();ans+=tmp;cntc-=tmp;cntd-=tmp;
	ans+=2*(cntc/2+cntd/2);cntc&=1;if(cntc)ans+=3;
	printf("%d\n",ans);
	return 0;
}

谢谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值