AtCoder-ARC080D Prime Flip——差分+哥德巴赫猜想
阿新 • • 發佈:2021-02-18
題目
題解
很巧妙的一道題。
直接操作顯然不好做,把陣列差分一下,變為只有每一段連續朝上的開頭一個和尾部的後一個為1,其餘位置為0,每次操作區間 [l,r] (要求 r-l 為奇素數)把 l 處和 r處的0/1翻轉,要求最後全部變為零。
容易發現差分後1的數量一定是偶數,所以一定是兩兩翻轉消去的,
我們簡單地對 r-l 分類討論一下,有三種情況:
- r-l 為奇素數,操作1次;
- r-l 為偶數,這時候怎麼辦呢?看到奇素數我們會想到小學三年級學過的哥德巴赫猜想:任何一個大於4的偶數都可以表示為兩個奇素數相加,如6=3+3,8=3=5。雖然是個猜想,但是至少在1e7以內是正確的。另外對於小於等於4的偶數2、4,2=5-3,4=7-3,所以此時操作僅需2次。
- r-l 為奇非素數,由於1=7-3-3,而除1以外的所有奇非素數-3過後都是大於4的偶數,所以需要操作3次。
然後我們需要把1兩兩匹配起來,可以先把1的位置按奇偶性分開,形成一個二分圖,差為奇素數的連邊,跑一次二分圖匹配(每條邊貢獻1),剩下的點按奇-奇、偶-偶兩兩匹配刪去,貢獻2,如果還有剩的,一定是一奇一偶且差為奇非素數,再算一個貢獻3。
用匈牙利+樸素判斷質數是,用尤拉篩是,
如果用DINIC跑匹配可以更快,總複雜度大概是或,
還可以把內的素數篩出然後用樸素判斷,複雜度
如果你還想更快,可以用Miller–Rabin演算法,但是沒必要啊,過了就行了
程式碼
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cmath> #include<vector> #include<queue> #define ll long long #define MAXN 100005 #define MAXX 10000000 #define INF 0x3f3f3f3f using namespace std; inline ll read(){ ll x=0;bool f=1;char s=getchar(); while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();} while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar(); return f?x:-x; } int n,m,a[205],ans; bool nop[MAXN]; vector<int>pr; inline int ads(int x){return x>0?x:-x;} inline void getprime(int n){ nop[0]=nop[1]=1; for(int a=2;a<=n;a++){ if(!nop[a])pr.push_back(a); for(int i=0;i<pr.size()&&pr[i]*a<=n;i++){ nop[pr[i]*a]=1; if(a%pr[i]==0)break; } } } inline bool isprime(int x){ if(x<MAXN-4)return !nop[x]; for(int i=0;i<pr.size()&&pr[i]*pr[i]<=x;i++) if(x%pr[i]==0)return 0; return 1; } int f[100005],cur[505],c,d,s1[205],s2[205],IN; int dt[505]; struct edge{ int v,id;edge(){} edge(int V,int I){v=V,id=I;} }; vector<edge>G[505]; queue<int>q; inline void addedge(int u,int v,int w){ f[IN]=w,f[IN^1]=0; G[u].push_back(edge(v,IN)),G[v].push_back(edge(u,IN^1)); IN+=2; } inline bool bfs(int S,int T){ while(!q.empty())q.pop(); memset(dt,-1,sizeof(dt)); dt[S]=0,q.push(S); while(!q.empty()){ int u=q.front();q.pop(); for(int i=0;i<G[u].size();i++) if(f[G[u][i].id]>0&&dt[G[u][i].v]<0) dt[G[u][i].v]=dt[u]+1,q.push(G[u][i].v); } return dt[T]>=0; } inline int dfs(int x,int lim,int T){ if(x==T)return lim; int res=lim; for(int i=cur[x];i<G[x].size()&&res>0;i++){ cur[x]=i; int v=G[x][i].v,a=G[x][i].id; if(f[a]>0&&dt[v]==dt[x]+1){ int ad=dfs(v,min(res,f[a]),T); f[a]-=ad,f[a^1]+=ad,res-=ad; } } return lim-res; } inline int dinic(int S,int T){ int res=0; while(bfs(S,T)){ memset(cur,0,sizeof(cur)); while(int ad=dfs(S,INF,T))res+=ad; } return res; } signed main() { n=read(); getprime(MAXN-5); for(int i=1,ls=-1;i<=n;i++){ int x=read(); if(x>ls+1)a[++m]=x,a[++m]=x+1; else a[m]++; ls=x; } for(int i=1;i<=m;i++){ if(a[i]&1)s1[++c]=a[i]; else s2[++d]=a[i]; } for(int i=1;i<=c;i++)addedge(0,i,1); for(int i=1;i<=d;i++)addedge(c+i,c+d+1,1); for(int i=1;i<=c;i++) for(int j=1,s;j<=d;j++){ s=ads(s1[i]-s2[j]); if((s&1)&&isprime(s))addedge(i,c+j,1); } int p=ans=dinic(0,c+d+1); ans+=c+d-p-p; if((c-p&1)&&(d-p&1))ans++; printf("%d\n",ans); return 0; }