BZOJ 2907: 拜访神犇
设最优策略为第一步向左走
那么肯定是走到最左边最优
需要补一些步数
一种是最右边的连着选,多出一倍代价
一种是不连着选,多出两倍代价
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int cnt,n,lim,s,root,sz[10000005],a[1000005],ls[10000005],rs[10000005]; long long ans,tr[10000005]; void update(int x){ sz[x]=sz[ls[x]]+sz[rs[x]]; tr[x]=tr[ls[x]]+tr[rs[x]]; } void insert(int &t,int l,int r,int x){ if (!t) t=++cnt; if (l==r){ sz[t]++; tr[t]+=x; return; } int mid=(l+r)>>1; if (x<=mid) insert(ls[t],l,mid,x); else insert(rs[t],mid+1,r,x); update(t); } long long query(int t,int l,int r,int x){ if (!x) return 0; if (!t) return 0; if (l==r) return 1ll*l*x; int mid=(l+r)>>1; if (sz[ls[t]]<=x) return query(rs[t],mid+1,r,x-sz[ls[t]])+tr[ls[t]]; else return query(ls[t],l,mid,x); } void solve(){ int Lim=lim; if (Lim>n-2) return; cnt=0; root=0; memset(ls,0,sizeof(ls)); memset(rs,0,sizeof(rs)); memset(tr,0,sizeof(tr)); memset(sz,0,sizeof(sz)); long long ANS=a[n]+a[s]; Lim-=s-1; if (Lim<=0) ans=min(ans,ANS); for (int i=s+1; i<n; i++){ insert(root,0,1e9,a[i+1]-a[i]); long long ANS1=ANS+a[n]-a[i+1]; int l=Lim-(n-(i+1)); if (l>0) ANS1+=query(root,0,1e9,l)*2; if (l>=0) ans=min(ans,ANS1); } } int main(){ scanf("%d%d%d",&n,&lim,&s); for (int i=1; i<=n; i++) scanf("%d",&a[i]); ans=1ll<<60; if (s!=n && n-2<lim || !lim || s==n && n-1<lim){ printf("-1\n"); return 0; } solve(); for (int i=1; i<=n/2; i++) swap(a[i],a[n-i+1]); for (int i=n; i>=1; i--) a[i]=a[1]-a[i]; lim=n-lim-1; s=n-s+1; solve(); if (ans!=1ll<<60) printf("%lld\n",ans); else printf("-1\n"); return 0; } /* 8 4 1 0 99 142 209 398 493 571 652 685 992 8 0 2 0 3 30 124 153 160 199 216 270 */