D. New Year Concert
题意:
定义一个序列是坏序列:
g
c
d
(
a
l
,
a
l
+
1
.
.
.
a
r
)
=
r
−
l
+
1
gcd(a_l,a_{l+1}...a_r) = r - l + 1
gcd(al,al+1...ar)=r−l+1,即区间
g
c
d
gcd
gcd 等于区间长度
给定一个序列
a
a
a,定义
f
(
i
)
f(i)
f(i) 是将
a
1
,
a
2
,
.
.
.
a
i
a_1,a_2,...a_i
a1,a2,...ai 这段前缀序列,变成不含坏序列的操作次数,每次操作可以选定任意一个数进行修改
思路:
g
c
d
(
)
gcd()
gcd() 里边的数不断增加时,
g
c
d
gcd
gcd 是非递增的
假设当前区间为
[
l
,
r
]
[l,r]
[l,r],此时
g
c
d
(
a
l
,
a
l
+
1
,
.
.
a
r
)
=
r
−
l
+
1
gcd(a_l,a_{l+1},..a_r)=r-l+1
gcd(al,al+1,..ar)=r−l+1 ,如果后拓展一个数,区间长度不断增加,而
g
c
d
gcd
gcd 非递增,往后不可能出现交集。
这时我们可以发现一个性质,一个端点
l
l
l,至多只存在一个端点
r
r
r 使得这个区间是坏的
这时我们可以考虑枚举左端点
l
l
l,二分右端点
r
r
r,找出所有可能的
b
a
d
bad
bad 区间,用
v
e
c
t
o
r
vector
vector 存下所有右端点对应的左端点,
可以采用
S
T
ST
ST 表预处理出区间
g
c
d
gcd
gcd 方便二分
剩下的问题是最小化修改次数,我们可以采用贪心策略
枚举右端点
i
i
i 考虑是否需要修改,对于该右端点的所有左端点,如果出现一个左端点大于上次修改的点,也就是之前的修改点没有包含在这个区间内,这时修改
a
i
a_i
ai 为大质数,答案
+
1
+1
+1,更新上一次修改的数的位置
code:
#include#define endl 'n' #define ll long long #define ull unsigned long long #define ld long double #define all(x) x.begin(), x.end() #define eps 1e-6 using namespace std; const int maxn = 2e5 + 9; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; ll n, m; int Log[maxn]; int stgcd[maxn][30]; int a[maxn]; vector v[maxn]; int find(int l, int r) { int s = Log[r - l + 1]; int d = __gcd(stgcd[l][s], stgcd[r-(1< > 1] + 1; cin >> n; for(int i = 1; i <= n; ++i){ cin >> a[i]; stgcd[i][0] = a[i]; } for(int j = 1; j <= 21; ++j) for(int i = 1; i + (1 << j) - 1 <= n; ++i) stgcd[i][j] = __gcd(stgcd[i][j-1], stgcd[i+(1<<(j-1))][j-1]); for(int i = 1; i <= n; ++i) { int l = i, r = n; while(l < r) { int mid = (l + r + 1) >> 1; int x = check(i, mid); if(x >= 0) l = mid; else r = mid - 1; } if(check(i, l) == 0) v[l].push_back(i); } int pos = -1, ans = 0; for(int i = 1; i <= n; ++i) { for(auto x : v[i]){ if(x > pos){ ++ans; pos = i; } } cout << ans << " "; } } int main() { ios::sync_with_stdio(0); // int TT;cin>>TT;while(TT--) work(); return 0; }



