codeforces 1295E

题目链接

题意

有一个排列p,需要先将其在中间分割开,分割成非空的两个部分,然后每个p[i]有一个权值a[i],意思是把p[i]从一个集合调整到另一个集合的代价是a[i],要求最后第一个集合里的所有数都比第二个集合里的数小。球最小花费

数据范围

p 2 e 5 , a [ i ] 1 e 9 |p|\le 2e5,a[i]\le 1e9

解法

考虑枚举最终第一个集合的大小,从0到n,首先在大小为0时,答案就是a[1](大小为n时答案就是a[n])。
现在需要着重处理的就是大小1~(n-1)的答案:
我们用线段树维护在每个位置断开的答案,枚举现在集合的大小,那么小于等于集合大小的数如果在当前位置的右边就会产生贡献,大于集合大小的数在左边会产生贡献。然后统计一下就好了

(程序copy了wustdio大佬的)

#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
#define lson o<<1
#define rson o<<1|1
#define mid (l+r)/2
using namespace std;
struct Tree
{
	ll s0,s1;
	ll ans;
}tree[800005];
int n,p[200005],pos[200005];
ll a[200005];
void pushup(int o)
{
	tree[o].s0=tree[lson].s0+tree[rson].s0;
	tree[o].s1=tree[lson].s1+tree[rson].s1;
	tree[o].ans=min(tree[lson].ans+tree[rson].s0,tree[rson].ans+tree[lson].s1);
	tree[o].ans=min(tree[o].ans,tree[lson].s1+tree[rson].s0);
}
void build(int o,int l,int r)
{
	if(l==r)
	{
		tree[o].s1=a[l];
		tree[o].ans=1e18;
		return;
	}
	build(lson,l,mid);
	build(rson,mid+1,r);
	pushup(o);
}
void update(int o,int l,int r,int p)
{
	if(l==r)
	{
		tree[o].s0=tree[o].s1;
		tree[o].s1=0;
		return;
	}
	if(p<=mid)update(lson,l,mid,p);
	else update(rson,mid+1,r,p);
	pushup(o);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&p[i]);
		pos[p[i]]=i;
	}
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
	build(1,1,n);
	ll ans=tree[1].ans;//ans=a[1] 
	for(int i=1;i<=n;i++)
	{
		update(1,1,n,pos[i]);
		ans=min(ans,tree[1].ans);
	}
	printf("%lld\n",ans);
	return 0;
}
发布了95 篇原创文章 · 获赞 9 · 访问量 3194

猜你喜欢

转载自blog.csdn.net/wmhtxdy/article/details/104117589