Description
给你一个完全图
i->j的无向边边权为 ∣ i − j ∣ ∗ D + A [ i ] + A [ j ] |i-j|*D+A[i]+A[j] ∣i−j∣∗D+A[i]+A[j]
求最小生成树
n<=2e5 D,A[i]<=1e9
题解
傻逼了一会…
完全图就直接无脑prim
维护一个线段树表示 [ i , j ] [i,j] [i,j]这个区间在集合里面连到集合外面最短的边是什么
显然我们需要维护更新
于是考虑怎么维护 [ l , m i d ] [l,mid] [l,mid]和 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]这一段的边
式子划一下就有 D ∗ i + A [ i ] + ( − D ∗ j + A [ j ] ) , ( i > j ) D*i+A[i]+(-D*j+A[j]),(i>j) D∗i+A[i]+(−D∗j+A[j]),(i>j)
再开两个线段树分别表示加入集合和没有加入集合的点的拆分贡献
讨论一下两边的东西就好了…
复杂度 n l o g n nlogn nlogn
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
#define lc now<<1
#define rc now<<1|1
using namespace std;
inline int read()
{
int f=1,x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int stack[20];
inline void write(LL x)
{
if(x<0){putchar('-');x=-x;}
if(!x){putchar('0');return;}
int top=0;
while(x)stack[++top]=x%10,x/=10;
while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}
const LL inf=1e18;
const int MAXN=200005;
int n,D,a[MAXN];
struct node
{
int p;LL c;
node(){}
node(int _p,LL _c){p=_p;c=_c;}
};
node _min(node u1,node u2){return u1.c<u2.c?u1:u2;}
struct segtree1
{
node mn[2][MAXN*4];
void buildtree(int now,int l,int r)
{
if(l==r){mn[0][now]=mn[1][now]=node(l,inf);return ;}
int mid=(l+r)/2;
buildtree(lc,l,mid);buildtree(rc,mid+1,r);
mn[0][now]=mn[1][now]=_min(mn[0][lc],mn[1][rc]);
}
void modify(int now,int l,int r,int p,node c,int opt)
{
if(l==r){mn[opt][now]=c;return ;}
int mid=(l+r)/2;
if(p<=mid)modify(lc,l,mid,p,c,opt);
else modify(rc,mid+1,r,p,c,opt);
mn[opt][now]=_min(mn[opt][lc],mn[opt][rc]);
}
node qry(int now,int l,int r,int ql,int qr,int opt)//0 左边 1 右边
{
if(l==ql&&r==qr)return mn[opt][now];
int mid=(l+r)/2;
if(qr<=mid)return qry(lc,l,mid,ql,qr,opt);
else if(mid+1<=ql)return qry(rc,mid+1,r,ql,qr,opt);
else return _min(qry(lc,l,mid,ql,mid,opt),qry(rc,mid+1,r,mid+1,qr,opt));
}
}seg1,seg2;//1 未加入 2 加入
struct segtree2
{
node mn[MAXN*4];
void buildtree(int now,int l,int r)
{
if(l==r){mn[now]=node(l,inf);return ;}
int mid=(l+r)/2;
buildtree(lc,l,mid);buildtree(rc,mid+1,r);
mn[now]=_min(mn[lc],mn[rc]);
}
void pushup(int now,int l,int r)
{
int mid=(l+r)/2;
mn[now]=_min(mn[lc],mn[rc]);
node u1=seg1.mn[0][lc],u2=seg2.mn[1][rc];
// u1=seg1.qry(1,1,n,l,mid,0),u2=seg2.qry(1,1,n,mid+1,r,1);
mn[now]=_min(mn[now],node(u1.p,u1.c+u2.c));
u1=seg1.mn[1][rc],u2=seg2.mn[0][lc];
// u1=seg1.qry(1,1,n,mid+1,r,1),u2=seg2.qry(1,1,n,l,mid,0);
mn[now]=_min(mn[now],node(u1.p,u1.c+u2.c));
}
void modify(int now,int l,int r,int p)
{
if(l==r){mn[now].c=inf;return ;}
int mid=(l+r)/2;
if(p<=mid)modify(lc,l,mid,p);
else modify(rc,mid+1,r,p);
pushup(now,l,r);
}
}seg3;
int main()
{
n=read();D=read();
if(n==1)return puts("0"),0;
for(int i=1;i<=n;i++)a[i]=read();
seg1.buildtree(1,1,n);
seg2.buildtree(1,1,n);
seg3.buildtree(1,1,n);
for(int i=2;i<=n;i++)
{
seg1.modify(1,1,n,i,node(i,(LL)-D*i+a[i]),0);
seg1.modify(1,1,n,i,node(i,(LL)D*i+a[i]),1);
}
seg2.modify(1,1,n,1,node(1,(LL)-D*1+a[1]),0);
seg2.modify(1,1,n,1,node(1,(LL)D*1+a[1]),1);
seg3.modify(1,1,n,1);
LL ans=0;
for(int i=1;i<n;i++)
{
ans+=seg3.mn[1].c;
int u=seg3.mn[1].p;
seg1.modify(1,1,n,u,node(u,inf),0);seg1.modify(1,1,n,u,node(u,inf),1);
seg2.modify(1,1,n,u,node(u,(LL)-D*u+a[u]),0);seg2.modify(1,1,n,u,node(u,(LL)D*u+a[u]),1);
seg3.modify(1,1,n,u);
}
pr2(ans);
return 0;
}