bzoj 1106: [POI2007]立方体大作战tet(贪心+树状数组)

1106: [POI2007]立方体大作战tet

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 785   Solved: 574
[ Submit][ Status][ Discuss]

Description

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

Input

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

Output

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

Sample Input

样例输入1
5
5 2 3 1 4 1 4 3 5 2
样例输入2
3
1 2 3 1 2 3

Sample Output

样例输出1
2
样例输出2
3

知道思路这题是非常好写的

思路:

贪心,遍历每个数,如果这个数字已经出现过了,答案加上这两个数之间其它数字的个数

并将这个数字从数列中删去就行了

例如:

1 2 3 2 3 1

当遍历到第4个数'2'时,中间有个3,ans++

当遍历到第5个数'3'时,中间没有数字(有个2但已经被删了)

当遍历到第6个数'1'时,中间也没有数字(2和3都被删了)

所以最后答案是1

上面的步骤同时也是证明:删除任意一对数时,一定要保证中间已经没有相同的数字了才是最优解

过程可以用树状数组维护

#include
#define LL long long
int n, flag[50005], tre[100005];
void Update(int k, int x)
{
	while(k<=n*2)
	{
		tre[k] += x;
		k += k&-k;
	}
}
int Query(int k)
{
	int ans = 0;
	while(k)
	{
		ans += tre[k];
		k -= k&-k;
	}
	return ans;
}
int main(void)
{
	LL ans;
	int i, x;
	scanf("%d", &n);
	ans = 0;
	for(i=1;i<=2*n;i++)
	{
		scanf("%d", &x);
		if(flag[x]==0)
		{
			Update(i, 1);
			flag[x] = i;
		}
		else
		{
			ans += Query(i)-Query(flag[x]);
			Update(flag[x], -1);
		}
	}
	printf("%lld\n", ans);
	return 0;
}
/*
3
1 2 3 2 3 1
*/


你可能感兴趣的:(#,各种水题)