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
#include
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;
}