Div2とはいえ、かなり簡単で完答者が多かった回。
https://codeforces.com/contest/1325/problem/E
問題
N個の正整数が与えられる。
ここからいくつか選び、積が平方数となるようんいしたい。
最小何個選べばよいか。
なお、各整数の約数は7個以下である。
解法
各整数あらかじめ平方数で割っておく。
すると各値は以下のいずれかとなる。
- 1
- 素数
- 2つの異なる素数の積
1つ目があればその1要素で即平方数完成である。
また、2番目は便宜上1×素数と考えると楽である。
そうすると2番目と3番目は2個の異なる整数の形を成すことがわかる。
そこで、各正整数を頂点する無向グラフを考える。
各正整数に対応する2個の整数の積に対し、その2点間に辺を張ろう。
あとはこのグラフで最小の閉路を求めればよい。
const int prime_max = 1100000; vector<int> prime; int NP,divp[prime_max]; void cprime() { if(NP) return; NP++; prime.push_back(1); for(int i=2;i<prime_max;i++) if(divp[i]==0) { //M[i]=NP; for(ll j=i;j>=i&&j<prime_max;j+=i) if(divp[j]==0) divp[j]=NP; prime.push_back(i); NP++; } } int N; int id; vector<int> C[101010]; int mi=2020202; set<int> S[1010101]; queue<int> Q; int D[90000]; void solve() { int i,j,k,l,r,x,y; string s; cprime(); cin>>N; int mi=10101010; FOR(i,N) { cin>>r; vector<int> V; V.push_back(0); while(r>1) { x=divp[r]; if(V.size() && V.back()==x) V.pop_back(); else V.push_back(x); r/=prime[x]; } if(V.size()==1) return _P("1\n"); if(V.size()==2) { x=V[0]; y=V[1]; } else { x=V[1]; y=V[2]; } if(S[x].count(y)) mi=2; C[x].push_back(y); C[y].push_back(x); S[x].insert(y); } if(mi<=2) return _P("%d\n",mi); FOR(i,NP) if(prime[i]>1 && prime[i]<=1000) { FOR(j,NP) D[j]=1010101; D[i]=0; Q.push(i); while(Q.size()) { x=Q.front(); Q.pop(); FORR(e,C[x]) { if(D[e]>=D[x]) mi=min(mi,D[x]+D[e]+1); if(D[e]>D[x]+1) { D[e]=D[x]+1; Q.push(e); } } } } if(mi>N) mi=-1; cout<<mi<<endl; }
まとめ
REALと言いつつ整数のみ。