0%

[LGR-128] 不见故人

什么?只是一道蓝题?

什么?我tm不会做?

什么?你告诉我是普及月赛T3?

题目链接

题意

给定一个长度为 的序列 ,可以进行如下操作若干次:

  1. 选定一个区间 ,令 ;
  2. 令所有的 。

一次操作的代价是 , 为给定常数。

求将所有数字变得相同的最小总代价。

题解

这篇题解用到了一个叫做 baka's trick 的技巧。

玛卡巴卡(

首先这道题还是要分析一些性质的。

性质 :最终所有数字一定都是 。

显然,第 个位置上的数字,最终一定是 的因数,而所有数字都要相等,所以他们必然都是所有数字的最大公倍数。

性质 :所有位置最多参与一次操作。

这个想一想大概也能想出来。

同一个位置被操作两次,显然不如取两次操作的并,直接操作一次。

设 。

这样我们大概就能设计出一个 dp:

表示区间只涉及 的位置,并将前 个位置都变成 的最小总代价。

考虑转移。

若初始就有 ,那么 可以不进行操作,就有转移 。

否则,枚举最右侧的操作区间为 ,一定有 。

暴力转移,时间复杂度为 ,可以拿到 分。

设 ,并对于每一个 ,二分出最大的,合法的 ,可以替代上面的转移:

时间复杂度 ,可以拿到 分。

baka's trick

不难发现,时间复杂度瓶颈主要在于二分与 gcd。

那么能不能加速这个寻找 的过程呢?

首先会发现,当 增长时, 单调不降,首先考虑双指针。

不难发现,我们双指针过程中,加入一个元素复杂度为 ,但是删除需要 ,很难蚌。

这个时候,就需要一个技巧,就是 baka's trick,不带删的双指针。

普通的双指针,我们会维护三个值 ,表示当前区间为 ,值为 。

进行 baka's trick 时,我们再额外维护一个值 ,并对于所有的 ,维护出 或 。

初始时,有 。

当右端点右移时,我们可以在 时间内更新出新的 。

使用 和 可以在 时间内算出当前区间 的权值。

当左端点左移时,若有 ,则令 ,并重新更新 。

可以证明,这个复杂度是均摊 的。

这样,我们就将寻找 的复杂度变为了 ,就可以进行我们上边的dp了。

总复杂度 ,可以通过这道题。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
inline int read()
{
int s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
ll dp[4000001];
ll f[4000001];
int a[4000001];
int v[4000001];
int l,r,mid;
int base;
int n,k;
inline int gcd(int a,int b)
{
int r=a%b;
while(r)
{
a=b;
b=r;
r=a%b;
}
return b;
}
inline void update()
{
int now=0;mid=r;
for(int i=r;i>=l;i--) v[i]=now=gcd(now,a[i]);
}
inline int get(int l,int r){ return gcd(v[l],v[r]);}
int main()
{
n=read(),k=read();
for(int i=1;i<=n;i++) a[i]=read(),base=gcd(base,a[i]);
l=mid=1;
for(int i=1;i<=n;i++)
{
r=i,v[r]=gcd(v[r-1],a[i]);
if(a[i]==base)
{
dp[i]=min(dp[i-1],f[i-1]+i+k);
f[i]=min(f[i-1],dp[i]-i);
continue;
}
while(get(l,r)==base)
{
l++;
if(mid<l) update();
}
if(l==1) dp[i]=inf;
else dp[i]=f[l-2]+i+k;
f[i]=min(f[i-1],dp[i]-i);
}
printf("%lld\n",dp[n]);
return 0;
}

感谢观看!