【数学思维】【树状数组】lydsy1106 [POI2007]立方体大作战tet

1106: [POI2007]立方体大作战tet
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 1046 Solved: 762
[Submit][Status][Discuss]
Description
  一个叫做立方体大作战的游戏风靡整个Byteotia。这个游戏的规则是相当复杂的,所以我们只介绍他的简单规
则:给定玩家一个有2n个元素的栈,元素一个叠一个地放置。这些元素拥有n个不同的编号,每个编号正好有两个
元素。玩家每次可以交换两个相邻的元素。如果在交换之后,两个相邻的元素编号相同,则将他们都从栈中移除,
所有在他们上面的元素都会掉落下来并且可以导致连锁反应。玩家的目标是用最少的步数将方块全部消除。

Input
  第一行包含一个正整数n(1<=n<=50000)。接下来2n行每行一个数ai,从上到下描述整个栈,保证每个数出现且
仅只出现两次(1<=ai<=n)。初始时,没有两个相同元素相邻。并且保证所有数据都能在1000000步以内出解。

Output
  第一行包含一个数m,表示最少的步数。


 一个很直接的想法:我们先定目标消去中间没有其余数对的数对,因为这是必须要消除的,然后一步步模拟连锁反应,但是模拟之后又要重新去找中间没有其余数对的数对,容易超时。
 观察一个数对,如果中间有其余数对,那么无视掉,因为它们必然会事先消除,然后两个数之间的距离-1,就是让这个数对消除需要的步数。而这个步数走完之后,可以让原先两者之间的数字离它们的目标靠近一步,总共也靠近了 两个数之间的距离-1。于是,我们只要求出 任意一个数对之间取出其余数对之后的距离-1 这个量的总和,最后除以2,即可得到答案。
 要查询两个数之间的数对数,可以从右到左扫描数对左端点,再在树状数组中加入右端点,查询区间中的右端点个数,就是包含的数对数。

#include<cstdio>
#include<set>
using namespace std;

int a[100005],pos[100005],n,ans;
bool f[100005],b[100005];

struct Finwick
{
	int C[100005],n;
	
	int sum(int x)
	{
		int res=0;
		while(x>0)
			res+=C[x],x-=x&-x;
		return res;
	}
	
	void add(int x, int d)
	{
		while(x<=n)
			C[x]+=d,x+=x&-x;
	}
}F;

int main()
{
	scanf("%d",&n);
	F.n=n*2;
	for(int i=1;i<=2*n;i++)
	{
		scanf("%d",&a[i]);
		if(!b[a[i]])
			b[a[i]]=f[i]=true;
		else
			pos[a[i]]=i;
	}
	for(int i=2*n;i>0;i--)
		if(f[i])
		{
			ans+=pos[a[i]]-i-1-F.sum(pos[a[i]])*2;
			F.add(pos[a[i]],1);
		}
	printf("%d",ans>>1);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值