Codeforces Round #446 (Div. 2) C. Pride
問題ページ
Problem - C - Codeforces
概要
長さnの数列aが与えられる。以下の操作を繰り返して数列のすべての要素を1にする最小の操作回数を求めろ。
操作: 数列aから隣接した2要素x,yを選び、その一方をgcd(x, y)と置き換える。
解法
まず、数列中に1が1つでも存在すれば gcd(1,x)=1 よりすべての要素を1に置き換えることが可能でn-(1の個数)が答えになる。
操作を繰り返して1つでも1をつくることができれば同様に数列中のすべての要素を1にすることができ、答えは(1をつくるまでにかかった回数)+n-1となる。逆に1をつくることが不可能であれば数列中のすべての要素を1にすることは不可能で-1を出力する。
したがって、1をつくるのにかかる最短の操作回数を求めたい。
i番目の要素から左・右に操作を続け、gcdが1になったタイミングで最短の操作回数の更新を行うことで可能でこれはO(N2)でできる。
スパーステーブルやセグ木で区間gcdを求められるようにしておくと尺取っぽくできてO(NlogN)で解けそう。
//#define __USE_MINGW_ANSI_STDIO 0 #include <bits/stdc++.h> using namespace std; typedef long long ll; #define int ll typedef vector<int> VI; typedef vector<VI> VVI; typedef vector<ll> VL; typedef vector<VL> VVL; typedef pair<int, int> PII; #define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i) #define REP(i, n) FOR(i, 0, n) #define ALL(x) x.begin(), x.end() #define IN(a, b, x) (a<=x&&x<b) #define MP make_pair #define PB push_back #ifdef int const ll INF = (1LL<<60); #else const int INF = (1LL<<30); #endif const double PI = 3.14159265359; const double EPS = 1e-12; const int MOD = 1000000007; template <typename T> T &chmin(T &a, const T &b) { return a = min(a, b); } template <typename T> T &chmax(T &a, const T &b) { return a = max(a, b); } int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0}; int a[2010]; ll gcd(ll a, ll b) { return b != 0 ? gcd(b, a%b) : a; } ll lcm(ll a, ll b) { return a/gcd(a, b)*b; } signed main(void) { int n; cin >> n; int one = 0; REP(i, n) { cin >> a[i]; if(a[i] == 1) one++; } if(n == 1) { if(a[0] == 1) cout << 0 << endl; else cout << -1 << endl; return 0; } if(one) { cout << n-one << endl; return 0; } bool flag = false; int ret = INF; REP(i, n) { int tmp = a[i]; FOR(j, i, n-1) { tmp = __gcd(tmp, a[j+1]); if(tmp == 1) chmin(ret, j-i+1); } tmp = a[i]; for(int j=i; j>0; --j) { tmp = __gcd(tmp, a[j-1]); if(tmp == 1) chmin(ret, i-j+1); } // cout << i << " " << ret << endl; } if(ret == INF) cout << -1 << endl; else cout << n+ret-1 << endl; return 0; }