【ARC080F】Prime Flip(二分图匹配,差分)
这种区间反转的题,套路就是差分。
设 表示第 枚硬币是否正面朝上,显然只有 等于 ,其他都是 。那么我们的目标是把 数组全部变成 。
设 表示第 枚硬币和第 枚硬币是否不同,即 。那么我们的目标就变成了把 数组全部变成 ,即让每个数都相同。
设 为所有奇数质数的集合,,那么一次反转可以看做将 全部取反。
发现将 取反后其实等价于 和 两个数取反。
所以现在操作就变成了:任意选择两个正整数 ,且满足 ,然后将 和 取反。
然后在所有值为枚举满足 的任意一对 (),考虑如何操作才能将 和 都变为 且操作数最少,分情况讨论:
-
。此时我们可以直接将 和 变为 ,共 次操作。
-
为正偶数。
那么当 时,我们可以对 、 取反,然后再对 、 取反,共 次操作;
当 时,我们可以对 、 取反,然后再对 、 取反,共 次操作;
当 为其他正偶数时,由哥德巴赫猜想在 范围内的正确性,可知 可以分为两个奇质数的和。即存在 ,且 。那么我们可以对 、 取反,然后再对 、 取反,共 次操作。
综述,当 为正偶数时,最小的操作数都是 次。
-
为除 以外的正奇数。
当 时,我们可以对 、 取反,然后对 、 取反,对 、 取反,共 次操作。
类似地,可知当 时,也存在一种方案且最小操作数为 。
当 为其他正奇数时,即 且 为奇数时,我们可以将 分解成 “” 的形式,然后再由 得此时最少的操作数为 次。
综述,当 为除 以外的正奇数时,最小的操作数都是 次。
所以为了使得操作数最少,我们应该优先使用第 种情况。
设有 个 ,并且将他们的下标用集合 表示。
那么 肯定是偶数,因为考虑将 中连续的 当做一个块。那么对于每个块 ,块头贡献一个 ,块尾贡献一个 ,所以总贡献就是偶数个。
发现如果有 ,那么 和 的奇偶性必定不同。
于是想到将 奇偶分类,并对于所有的 ,连边 ,那么这就是一个二分图的形式。
显然对这个二分图跑最大匹配就是第 种情况的最多使用数。
将这些点取反之后,还剩下一些点需要取反,于是考虑使用第 种情况。
显然,若 为正偶数,那么 和 的奇偶性相同。
所以将奇类中剩下的 进行两两取反,将偶类中剩下的 进行两两取反。
然后再考虑第 种情况,此时奇类和偶类肯定要么都剩下 个未取反、要么都剩下 个未取反,因为 为偶数。然后对于各剩下 个未取反的情况下,将他们两个按第 种情况处理。
将 种情况的总操作数加起来,就是最后的答案了。
代码如下:
#include<bits/stdc++.h>
#define N 110
#define INF 0x7fffffff
using namespace std;
int n,a[N];
int tot,odd,b[N<<1];
int cnt=1,head[N<<1],cur[N<<1],nxt[N*N*8+N*4],to[N*N*8+N*4],c[N*N*8+N*4];
int s,t,num[N<<1];
queue<int>q;
void adde(int u,int v,int ci)
{
to[++cnt]=v;
c[cnt]=ci;
nxt[cnt]=head[u];
head[u]=cnt;
to[++cnt]=u;
c[cnt]=0;
nxt[cnt]=head[v];
head[v]=cnt;
}
bool check(int x)//判断一个数是否属于prime
{
if(x<=2) return false;
if(!(x&1)) return false;
for(int i=2,maxn=sqrt(x);i<=maxn;i++)
if(!(x%i)) return false;
return true;
}
bool bfs()
{
memcpy(cur,head,sizeof(cur));
memset(num,-1,sizeof(num));
q.push(s);
num[s]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(c[i]&&num[v]==-1)
{
num[v]=num[u]+1;
q.push(v);
}
}
}
return num[t]!=-1;
}
int dfs(int u,int minflow)
{
if(u==t||!minflow) return minflow;
int preflow=0,nowflow;
for(int i=cur[u];i;i=nxt[i])
{
cur[u]=i;
int v=to[i];
if(num[v]==num[u]+1&&(nowflow=dfs(v,min(c[i],minflow-preflow))))
{
preflow+=nowflow;
c[i]-=nowflow;
c[i^1]+=nowflow;
if(!(minflow-preflow)) break;
}
}
return preflow;
}
int dinic()//用dinic跑最大匹配
{
int maxflow=0;
while(bfs())
maxflow+=dfs(s,INF);
return maxflow;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
b[++tot]=a[1];
for(int i=2;i<=n;i++)
{
if(a[i]-a[i-1]>1)
{
b[++tot]=a[i-1]+1;//块尾贡献
b[++tot]=a[i];//块头贡献
}
}
b[++tot]=a[n]+1;
s=1,t=1+tot+1;
for(int i=1;i<=tot;i++)
{
if(b[i]&1)
{
odd++;
adde(s,1+i,1);
for(int j=1;j<=tot;j++)
if(check(abs(b[i]-b[j]))) adde(1+i,1+j,1);
}
else adde(1+i,t,1);
}
int sum1=dinic();
printf("%d\n",sum1+(odd-sum1)/2*2+((tot-odd)-sum1)/2*2+((odd-sum1)&1)*3);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?