CodeForces 387E George and Cards

47 篇文章 0 订阅
28 篇文章 0 订阅

题意:

有一串数字  主人公可以从这串数字中找出一个连续的子集扔掉其中最小的数字并得到与子集中元素个数相等数量的香肠  现在给出这串数字最后的状态  问  主人公将原串扔成目标串的样子的同时最多拿到多少香肠


思路:

为了拿到尽量多的香肠  每次选出的子集元素要尽量多  也就是要删除的元素要尽量小  这就提供了一个思路  从小往大删除元素  这个思路是有指导性的第一步

然后问题就变成  每次删除元素时  要找到该元素所在的尽量长的一串数字且保证要删除的数字是这里最小的  这可以抽象为  选择适当长度的线段  的问题  想到线段当然就联想到线段树或树状数组  这是第二步确定数据结构

最后就是实现  由于每个元素都不同  所以可以记录每个元素在数组中的位置  当要删除它的时候  可以直接找到它的位置  并尽量向左右拉长线段  灵活运用树状数组求和功能可以判断所检查的线段中是否含有曾经删除的元素或者比要删除元素小的元素  但是一个数字一个数字的检查实在复杂度太大  所以这里还需要二分查找的算法


代码:

#include<cstdio>
using namespace std;
#define M 1000005
#define lowbit(x) ( x&(-x) )

int n,m;
int a[M],vis[M],pos[M];
__int64 c[M];

void add(int x,__int64 key)
{
	while(x<=n)
	{
		c[x]+=key;
		x+=lowbit(x);
	}
}

__int64 sum(int x)
{
	__int64 tmp=0;
	while(x)
	{
		tmp+=c[x];
		x-=lowbit(x);
	}
	return tmp;
}

int main()
{
	int i,j,k,L,R,ansk;
	__int64 ans=0,tmp;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		add(i,1LL);
		pos[a[i]]=i;
	}
	for(i=1;i<=m;i++)
	{
		scanf("%d",&j);
		vis[j]=1;
	}
	for(i=1;i<=n;i++)
	{
		if(vis[i]) add(pos[i],-10LL*M);
		else
		{
			L=1;
			R=n;
			ansk=j=pos[i];
			tmp=sum(j);
			while(L<=j)
			{
				k=(L+j)>>1;
				if(sum(k)>tmp) L=k+1;
				else
				{
					ansk=k;
					j=k-1;
				}
			}
			if(sum(ansk)-sum(ansk-1)>=0) ansk--;
			L=ansk;
			ansk=j=pos[i];
			while(j<=R)
			{
				k=(R+j)>>1;
				if(sum(k)<tmp) R=k-1;
				else
				{
					ansk=k;
					j=k+1;
				}
			}
			R=ansk;
			ans+=sum(R)-sum(L);
			add(pos[i],-1);
		}
	}
	printf("%I64d\n",ans);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值