[图论记录]AT2689 [ARC080D] Prime Flip

command_block

2021-05-09 15:30:00

Personal

**题意** :有无限枚硬币,其中有 $n$ 枚硬币 $x_{1\ldots n}$ 初始时正面朝上,其余均为背面朝上。 每次可以选择一段区间 $[l,r]$ ,将区间内所有硬币翻转,其中 $r-l+1$ 为奇质数。 问最少多少次操作能使得所有硬币全部为背面朝上。可以证明一定有解。 $n\leq 100,x\leq 10^7$ ,时限$\texttt{2s}$。 ------------ 先将原序列差分,问题变为可以在两个差为奇质数的位置 $\rm xor\ 1$ ,多少次能使得整个序列变为全 $0$。 考虑将 $1$ 成对解决。对于两个位置 $x,y$ : - 若 $|x-y|$ 为奇质数 : $1$ 次操作。 - 若 $|x-y|$ 为偶数 : 由哥德巴赫猜想,$2$ 次操作。 - 若 $|x-y|$ 为奇合数 : 先随便整一个质数,然后转化为偶数的情况,$3$ 次操作。 (不难发现 $1$ 有偶数个,故一定有解) 于是,问题就被转化为了特殊的一般图最大权匹配。 由于权值的特殊性,可以贪心。 先尽量添加价值为 $1$ 的匹配,这似乎也是一个一般图最大匹配,不过由于位置的差值是奇(质)数,可以黑白染色变为二分图匹配。 (若拆掉一个价值为 $1$ 的匹配,利用剩余的 $2,3$ 边只会得到更劣的方案) 将被匹配掉的点取出。剩余的图中,也可以贪心地添加价值为 $2$ 的匹配。 将所有点按照奇偶性分为两部分,每一部分内部都充满了 $2$ 边,尽量配对即可。 使用 Dinic 求解二分图匹配,复杂度 $O(n^{2.5}+x)$。 ```cpp #include<cstdio> #include<vector> #include<queue> #define pb push_back #define INF 1000000000 #define MaxS 10005000 #define Maxn 205 using namespace std; struct Line{int t,cap,b;}; vector<Line> g[Maxn]; void adl(int f,int t,int cap){ g[f].pb((Line){t,cap,g[t].size()}); g[t].pb((Line){f,0,g[f].size()-1}); } int S,T,dis[Maxn],cur[Maxn]; bool bfs() { static queue<int> q; for (int i=1;i<=T;i++){cur[i]=dis[i]=0;} q.push(S);dis[S]=1; while(!q.empty()){ int u=q.front();q.pop(); for (int i=0,v;i<g[u].size();i++) if (g[u][i].cap&&!dis[v=g[u][i].t]){ dis[v]=dis[u]+1; if (v==T){ while(!q.empty())q.pop(); return 1; } q.push(v); } }return dis[T]; } int dfs(int u,int flow) { if (u==T)return flow; int sum=0; for (int &i=cur[u],v;i<g[u].size();i++) if (dis[v=g[u][i].t]==dis[u]+1&&g[u][i].cap){ int sav=dfs(v,min(flow,g[u][i].cap)); if (sav){ g[u][i].cap-=sav; g[v][g[u][i].b].cap+=sav; sum+=sav;flow-=sav; if (!flow)return sum; }else dis[v]=-1; } return sum; } bool e[MaxS]; void sieve(int n) { e[1]=1; for (int i=2;i<=n;i+=2)e[i]=1; for (int i=3;i*i<=n;i++) for (int j=i*i;j<=n;j+=i+i) e[j]=1; } int n,x[Maxn],tn; int main() { scanf("%d",&n);sieve(10000000); for (int i=1,xx;i<=n;i++){ scanf("%d",&xx); x[i*2-1]=xx;x[i*2]=xx+1; } for (int i=1;i<=n+n;i++) if (x[i]==x[i+1])i++; else x[++tn]=x[i]; S=tn+1;T=tn+2;int c0=0,c1=0; for (int i=1;i<=tn;i++){ if (x[i]&1){adl(S,i,1);c1++;} else {adl(i,T,1);c0++;} for (int j=i+1;j<=tn;j++) if (!e[x[j]-x[i]]) if (x[i]&1)adl(i,j,1); else adl(j,i,1); } int ans=0; while(bfs())ans+=dfs(S,INF); c0-=ans;c1-=ans; printf("%d",ans+c0/2*2+c1/2*2+(c0&1)*3); return 0; } ```