ARC080D Prime Flip 题解
题目描述
有无限枚硬币,其中恰有 \(n\) 枚硬币初始正面朝上,位置为 \(x_1,\cdots,x_n\) 。
你每次可以选择一段区间 \([l,r]\) ,要求 \(r-l+1\) 是奇质数,然后翻转区间中的所有硬币。
求最少的操作次数,使得所有硬币都背面朝上。
数据范围
- \(1\le n\le 100,1\le x_1\lt\cdots\lt x_n\le 10^7\) 。
时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{256MB}\) 。
分析
记奇质数集合为 \(\text{prime}\) 。
显然需要差分,那么每次操作等价于选择 \(p\in\text{prime}\) ,然后给 \(a_l\) 和 \(a_{l+p}\) 都异或上\(1\)。
记差分数组为 \(b_i\) ,显然 \(b\) 中有偶数个 \(1\) ,并且个数 \(\le 2n\) 。
考虑将这些点两两匹配消去。
假设现在要消两个距离为 \(d\) 的点,那么需要用若干个 \(\text{prime}\) 中的数通过加减凑出 \(d\) 。
- \(d\in\text{prime}\) :显然代价为 \(1\) 。
- \(2\mid d\) :代价为 \(2\) 。这是因为哥德巴赫猜想在 \(10^7\) 范围内成立,注意可以使用减法。
- \(else\) :代价为 \(3\) 。
这是一个完全图最小权完美匹配问题,由于不是二分图,所以似乎无从下手。
但事实上,根据边权的特殊性质,我们可以贪心地连尽量多权值为 \(1\) 的边(由于每条权值为 \(1\) 的边两端奇偶性不同,所以可以二分图最大匹配解决),然后用 \(2\) 的代价贪心匹配奇偶性相同的点,最后用不超过 \(1\) 条权值为 \(3\) 的边解决剩下的点即可。
时间复杂度 \(\mathcal O(n^{2.5})\) 。
#include<bits/stdc++.h>
using namespace std;
const int maxn=205,maxm=3e4,maxv=1e7+5,inf=1e9;
int n,s,t,x,k1,k2,res,tot=1;
int a[maxv],c[2][maxn];
int b[maxv],p[maxv];
int head[maxn],to[maxm],f[maxm],nxt[maxm];
int d[maxn];
void init(int n)
{
for(int i=2,cnt=0;i<=n;i++)
{
if(!b[i]) p[++cnt]=i;
for(int j=1;j<=cnt&&i*p[j]<=n;j++)
{
b[i*p[j]]=1;
if(i%p[j]==0) break;
}
}
b[1]=b[2]=1;
}
void addedge(int u,int v,int w)
{
nxt[++tot]=head[u],to[tot]=v,f[tot]=w,head[u]=tot;
nxt[++tot]=head[v],to[tot]=u,f[tot]=0,head[v]=tot;
}
bool bfs()
{
queue<int> q;
memset(d,0,sizeof(d));
d[s]=1,q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(f[i]&&!d[v])
{
d[v]=d[u]+1,q.push(v);
if(v==t) return true;
}
}
}
return false;
}
int dfs(int u,int limit)
{
if(u==t) return limit;
int flow=0;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(!f[i]||d[v]!=d[u]+1) continue;
int k=dfs(v,min(f[i],limit-flow));
if(!k) d[v]=0;
f[i]-=k,f[i^1]+=k,flow+=k;
if(flow==limit) break;
}
return flow;
}
int dinic()
{
int flow=0;
while(bfs()) flow+=dfs(s,inf);
return flow;
}
int main()
{
scanf("%d",&n),init(maxv-5);
for(int i=1;i<=n;i++) scanf("%d",&x),a[x]^=1,a[x+1]^=1;
for(int i=1;i<maxv;i++)
{
if(!a[i]) continue;
if(i&1) c[0][++k1]=i;
else c[1][++k2]=i;
}
s=0,t=k1+k2+1;
for(int i=1;i<=k1;i++) addedge(s,i,1);
for(int i=1;i<=k2;i++) addedge(k1+i,t,1);
for(int i=1;i<=k1;i++)
for(int j=1;j<=k2;j++)
if(!b[abs(c[0][i]-c[1][j])])
addedge(i,k1+j,1);
res=dinic(),k1-=res,k2-=res;
res+=(k1/2+k2/2)*2,k1%=2,k2%=2;
if(k1&&k2) res+=3;
printf("%d\n",res);
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/16608030.html
浙公网安备 33010602011771号