有史以来第一次rating为正..手速之力是无穷的!(雾)
这题还是蛮有趣..想到了区间DP但是发现转移不动,还是naive..
来看看题意。一串数,每次只能将相邻两个元素中的一个变成1,问要求最少多少次GCD才能全为1。如果不能,就输出-1。
如:
[2, 2, 3, 4, 6]变换:
- [2, 1, 3, 4, 6]
- [2, 1, 3, 1, 6]
- [2, 1, 1, 1, 6]
- [1, 1, 1, 1, 6]
- [1, 1, 1, 1, 1]
需要5次。
暴搜复杂度显然会爆。
考虑如果数列中含有
1
,那么显然
顺着这个思路,我们就是找出数列中变幻出第一个
1
的最少次数。
看一眼范围,
因此我们换一种状态表示方法:
而转移也很显然:
dp[L][R]=gcd(dp[L][R−1],s[R])
求出这个之后,我们的结果显然就是
R−L+n−1=R−L+n−1
,因为每次递推过程都可以看做进行一次gcd,所以区间长度表示gcd次数。
这样这个题是可以过的。
在讨论区偶然看到,这道题可以优化到
O(nlogn)
,用数据结构维护。
看到之后也是深深被折服,解法确实漂亮。
讨论帖给出的地址:http://paste.ubuntu.com/25983311/
考虑一下我们维护的是什么,显然是届到
1
需要的次数。
因此我们可以用map来维护达到某一个因数最后的位置,并不断更新一段区间达到的因数。来举个例子吧,以样例2 2 3 4 6为例
首先输入
然后输入
2
,
然后输入
3
,
此时,由于达到了因数
1
,因此可以更新变量
再输入
以此类推即可,最后输出
n+res−1
。
但是这样很不方便实现更新操作,因此我们可以另外开一个
e
集合,来存储每一次
总感觉是个玄学复杂度..有dalao 帮忙分析一下吗(逃)
在下的AC 抄写 代码:
#include <iostream>
#include <cstdio>
#include <climits>
#include <map>
using namespace std;
typedef long long LL;
typedef map<LL, LL> mll;
typedef mll::iterator mlt;
#define X first
#define Y second
#define INF INT_MAX >> 2
LL n;
mll upd, tmp;
inline void read(LL &x) {
x = 0; char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
inline LL gcd(LL x, LL y) {
return y == 0 ? x : gcd(y, x % y);
}
int main() {
read(n);
LL now, k, res = INF, cntone = 0;
for(int i = 1; i <= n; ++i) {
read(now);
if(now == 1) ++cntone;
upd.clear();
upd[now] = i;
for(mlt it = tmp.begin(); it != tmp.end(); ++it) {
k = gcd(it->X, now);
upd[k] = max(upd[k], it->Y);
}
tmp.clear();
for(mlt it = upd.begin(); it != upd.end(); ++it) {
tmp[it->X] = it->Y;
if(it->X == 1) {
res = min(res, i - it->Y);
}
}
}
if(cntone) cout<< n - cntone<<endl;
else if(res == INF) cout<<"-1"<<endl;
else cout<<res + n - 1<<endl;
return 0;
}