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
*/

  

posted @ 2018-12-01 19:45  ~Silent  阅读(190)  评论(0编辑  收藏  举报
Live2D