题目
思路
假设所有
A
i
A_i
Ai 不同,那么一条边肯定是
A
A
A 一边小,一边大
假设
j
<
i
,
A
j
<
A
i
j<i,A_j<A_i
j<i,Aj<Ai 并且此时
j
j
j 是
i
i
i 在左边值比
A
i
A_i
Ai 小的最小边
尝试证明只用保留这种边
O
(
n
)
O(n)
O(n)
当存在
k
k
k 满足
A
k
>
A
i
A_k>A_i
Ak>Ai 我们认为此时是
i
i
i 的选择方式,和
k
k
k 无关
当
i
<
k
i<k
i<k 时候是右边一条边的问题
也就是现在有两个问题
已知
(
j
,
i
)
<
(
k
,
i
)
(j,i)<(k,i)
(j,i)<(k,i)
A
j
,
A
k
<
A
i
A_j,A_k<A_i
Aj,Ak<Ai
那么
A
j
−
j
∗
D
<
A
k
−
k
∗
D
A_j-j*D<A_k-k*D
Aj−j∗D<Ak−k∗D
尝试证明
(
i
,
k
)
(i,k)
(i,k) 是这个三元环中最大的一条边
k
k
k 在
j
j
j 左边易证
j
<
k
<
i
j<k<i
j<k<i 时候
分析法后步骤:
A
i
+
i
D
>
A
k
+
k
D
A_i+iD>A_k+kD
Ai+iD>Ak+kD
A
i
+
i
D
−
k
D
>
A
k
+
k
D
−
k
D
>
A
j
−
j
D
+
k
D
A_i+iD-kD>A_k+kD-kD>A_j-jD+kD
Ai+iD−kD>Ak+kD−kD>Aj−jD+kD
(
k
,
i
)
>
(
j
,
i
)
(k,i)>(j,i)
(k,i)>(j,i)
然后根据破圈定理可得这条边一定不选
等于时候可知最大边随便一条不选就行了
右边同理
然后就只需要树状数组维护前缀最小值和对应下标
边数
O
(
n
)
O(n)
O(n) 时间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
代码
#include<set>
#include<map>
#include<stack>
#include<ctime>
#include<cstdio>
#include<queue>
#include<cmath>
#include<vector>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
int read(){
int f=1,x=0;char c=getchar();
while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
return f*x;
}
#define MAXN 200000
#define mp make_pair
#define INF 10000000000000000ll
int n,m;
pair<LL,int> tr[MAXN+5];
int lowbit(int x){return x&-x;}
void Init(){
for(int i=1;i<=m;i++)
tr[i]=mp(INF,0);
return ;
}
void Add(int i,pair<LL,int> x){
while(i<=m)
tr[i]=min(tr[i],x),i+=lowbit(i);
return ;
}
pair<LL,int> Que(int i){
pair<LL,int> ret=mp(INF,0);
while(i)
ret=min(ret,tr[i]),i-=lowbit(i);
return ret;
}
LL D,a[MAXN+5],b[MAXN+5];
struct E{
int u,v;
LL w;
friend bool operator < (E a,E b){return a.w<b.w;}
}edge[2*MAXN+5];
int fa[MAXN+5];
int Find(int x){return fa[x]==x?x:(fa[x]=Find(fa[x]));}
int main(){
n=read(),D=read();
for(int i=1;i<=n;i++)
a[i]=b[i]=read();
sort(b+1,b+n+1);
m=unique(b+1,b+n+1)-(b+1);
for(int i=1;i<=n;i++)
a[i]=lower_bound(b+1,b+m+1,a[i])-b;
int ecnt=0;
Init();
for(int i=1;i<=n;i++){
pair<LL,int> t=Que(a[i]);
if(t.second)
edge[++ecnt]=(E){i,t.second,t.first+b[a[i]]+i*D};
Add(a[i],mp(b[a[i]]-i*D,i));
}
Init();
for(int i=n;i>=1;i--){
pair<LL,int> t=Que(a[i]);
if(t.second)
edge[++ecnt]=(E){i,t.second,t.first+b[a[i]]-i*D};
Add(a[i],mp(b[a[i]]+i*D,i));
}
sort(edge+1,edge+ecnt+1);
for(int i=1;i<=n;i++)
fa[i]=i;
LL ans=0;
for(int i=1;i<=ecnt;i++){
int u=edge[i].u,v=edge[i].v;
LL w=edge[i].w;
int fu=Find(u),fv=Find(v);
if(fu!=fv){
fa[fu]=fv;
ans+=w;
}
}
printf("%lld\n",ans);
return 0;
}