LightOJ-1356 Prime Independence 質因子分解+二分圖最大獨立集
阿新 • • 發佈:2018-12-16
感覺這道題挺好的,數論+圖論。就是我太菜了,怎麼都寫不對啊啊啊55555... 先一搜題目,發現這道題用質因子分解+二分圖最大獨立集,好巧妙啊_(:з」∠)_ 二分圖最大獨立集=頂點數-最大匹配數(用Hopcroft-Carp演算法,匈牙利演算法會TLE...) 然後自己琢磨出如下思路:(用到了“拆點”)
但是仍然TLE了QAQ...附上TLE的程式碼:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<vector> #include<map> #include<queue> using namespace std; #define pb push_back const int INF=0x3f3f3f3f; const int MAXN=40005; int n,a[MAXN]; map<int,int>mp; const int MAX=500005; int pri[MAX+1]; void getp()//得到MAX內的素數 { memset(pri,0,sizeof(pri)); for(int i=2;i<MAX;i++) { if(!pri[i]) pri[++pri[0]]=i; for(int j=1;j<=pri[0]&&i*pri[j]<MAX;j++) { pri[i*pri[j]]=1; if(i%pri[j]==0) break; } } } vector<int>g[MAXN]; int un; int mx[MAXN],my[MAXN]; int dx[MAXN],dy[MAXN]; int dis; bool used[MAXN]; bool SearchP() { queue<int>Q; dis=INF; memset(dx,-1,sizeof(dx)); memset(dy,-1,sizeof(dy)); for(int i=0;i<un;i++) if(mx[i]==-1) { Q.push(i); dx[i]=0; } while(!Q.empty()) { int u=Q.front(); Q.pop(); if(dx[u]>dis) break; for(int i=0;i<g[u].size();i++) { int v=g[u][i]; if(dy[v]==-1) { dy[v]=dx[u]+1; if(my[v]==-1) dis=dy[v]; else { dx[my[v]]=dy[v]+1; Q.push(my[v]); } } } } return dis!=INF; } bool dfs(int u) { for(int i=0;i<g[u].size();i++) { int v=g[u][i]; if(!used[v]&&dy[v]==dx[u]+1) { used[v]=true; if(my[v]!=-1&&dy[v]==dis) continue; if(my[v]==-1||dfs(my[v])) { my[v]=u; mx[u]=v; return true; } } } return false; } int MaxMatch() { int res=0; un=n; memset(mx,-1,sizeof(mx)); memset(my,-1,sizeof(my)); while(SearchP()) { memset(used,false,sizeof(used)); for(int i=0;i<un;i++) if(mx[i]==-1&&dfs(i)) res++; } return res; } int main() { int t; scanf("%d",&t); getp(); for(int tt=1;tt<=t;tt++) { scanf("%d",&n); for(int i=0;i<=n;i++) g[i].clear(); int ma=-1; for(int i=0;i<n;i++) { scanf("%d",&a[i]); ma=max(ma,a[i]); } for(int i=0;i<ma;i++)//注意不是到n!到ma! mp[i]=-1;//因為下標從0開始 for(int i=0;i<n;i++) mp[a[i]]=i; //建圖 for(int i=0;i<n;i++) { for(int j=1;j<=pri[0];j++) { if(pri[j]>a[i]) break; if(a[i]%pri[j]==0) { int k=a[i]/pri[j]; //cout<<"i="<<i<<" a[i]="<<a[i]<<" pri[j]="<<pri[j]<<" k="<<k<<" mp[k]="<<mp[k]<<endl; if(mp[k]==-1) continue; g[i].pb(mp[k]); g[mp[k]].pb(i); } } } int res=MaxMatch(); //cout<<"res="<<res<<endl; int ans=(n*2-res)/2; printf("Case %d: %d\n",tt,ans); } return 0; }
後來想想應該是建圖部分複雜度有點高了,於是進行優化,只搜到sqrt(a[i])。以sqrt(a[i])為界,分為質數×合數、質數×質數、合數×質數三種情況,然後注意特判1。另外加上了一個判斷MAX內的數字是否為素數的函式和陣列。附上部分改後的程式碼:
bool isp[MAX]; void init()//判斷是否為素數 { memset(isp,true,sizeof(isp)); isp[0]=isp[1]=false; for(int i=2;i<MAX;i++) { if(isp[i]) for(int j=i+i;j<MAX;j+=i) isp[j]=false; } }
//建圖 for(int i=0;i<n;i++) { for(int j=1;j<=pri[0];j++) { if(pri[j]>sqrt(1.0*a[i])) break; if(a[i]%pri[j]==0) { int k=a[i]/pri[j]; if(mp[k]!=-1) { g[i].pb(mp[k]); g[mp[k]].pb(i); } if(k*pri[j]==a[i]) break; if(isp[k]&&mp[pri[j]]!=-1)//如果k為質數才加邊 { g[i].pb(mp[pri[j]]); g[mp[pri[j]]].pb(i); } } } //解決4,97,388這種情況 for(int k=4;k<=sqrt(1.0*a[i]);k++) { if(mp[k]==-1) continue; if(isp[k])//上邊掃過了 continue; if(a[i]%k) continue; if(isp[a[i]/k]) { g[i].pb(mp[k]); g[mp[k]].pb(i); } } } if(mp[1]!=-1)//特判1 for(int i=0;i<n;i++) if(isp[a[i]]) { g[mp[1]].pb(i); g[i].pb(mp[1]); }
但最後還是TLE了55555...沒辦法去搜網上的題解,發現大部分都說用“奇偶性建圖”,好神奇的操作_(:з」∠)_ 但是我不會啊QAQ...好不容易找到了拆點建圖的題解,附上大佬部落格Orz:
發現其實就是自己質因子分解弄複雜了。。用到Pollard Rho演算法,附上講解部落格Orz:
改了改,就過了。。另外這次我把點集的起始下標改為從1開始了(方便map初始化)。 附上AC程式碼:(真不容易啊QAQ...我好菜啊啊啊55555...)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
using namespace std;
#define pb push_back
const int INF=0x3f3f3f3f;
const int MAXN=40005;
int n,a[MAXN];
map<int,int>mp;
const int MAX=500005;
int pri[MAX+1];
void getp()//得到MAX內的素數
{
memset(pri,0,sizeof(pri));
for(int i=2;i<MAX;i++)
{
if(!pri[i])
pri[++pri[0]]=i;
for(int j=1;j<=pri[0]&&i*pri[j]<MAX;j++)
{
pri[i*pri[j]]=1;
if(i%pri[j]==0)
break;
}
}
}
vector<int>g[MAXN];
int un;
int mx[MAXN],my[MAXN];
int dx[MAXN],dy[MAXN];
int dis;
bool used[MAXN];
bool SearchP()
{
queue<int>Q;
dis=INF;
memset(dx,-1,sizeof(dx));
memset(dy,-1,sizeof(dy));
for(int i=1;i<=un;i++)
if(mx[i]==-1)
{
Q.push(i);
dx[i]=0;
}
while(!Q.empty())
{
int u=Q.front();
Q.pop();
if(dx[u]>dis)
break;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(dy[v]==-1)
{
dy[v]=dx[u]+1;
if(my[v]==-1)
dis=dy[v];
else
{
dx[my[v]]=dy[v]+1;
Q.push(my[v]);
}
}
}
}
return dis!=INF;
}
bool dfs(int u)
{
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(!used[v]&&dy[v]==dx[u]+1)
{
used[v]=true;
if(my[v]!=-1&&dy[v]==dis)
continue;
if(my[v]==-1||dfs(my[v]))
{
my[v]=u;
mx[u]=v;
return true;
}
}
}
return false;
}
int MaxMatch()
{
int res=0;
un=n;
memset(mx,-1,sizeof(mx));
memset(my,-1,sizeof(my));
while(SearchP())
{
memset(used,false,sizeof(used));
for(int i=1;i<=un;i++)
if(mx[i]==-1&&dfs(i))
res++;
}
return res;
}
int fac[MAXN];
void addedge(int num,int id)
{
int tmp=num;
int tot=0;
for(int j=1;j<=pri[0];j++)
{
if(pri[j]*pri[j]>tmp)
break;
if(tmp%pri[j]==0)
{
fac[tot++]=pri[j];
while(tmp%pri[j]==0)
tmp/=pri[j];
}
}
if(tmp>1)//最後一個質因數
fac[tot++]=tmp;
for(int i=0;i<tot;i++)
{
int x=num/fac[i];
//cout<<"i="<<i<<" fac="<<fac[i]<<" x="<<x<<endl;
if(mp[x])
{
g[id].pb(mp[x]);
g[mp[x]].pb(id);
}
}
}
int main()
{
int t;
scanf("%d",&t);
getp();
for(int tt=1;tt<=t;tt++)
{
scanf("%d",&n);
for(int i=0;i<=n;i++)
g[i].clear();
mp.clear();
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
mp[a[i]]=i;
}
for(int i=1;i<=n;i++)
addedge(a[i],i);
int res=MaxMatch();
//cout<<"res="<<res<<endl;
int ans=(n*2-res)/2;
printf("Case %d: %d\n",tt,ans);
}
return 0;
}