【BZOJ1106】【POI2007】立方体大作战tet(贪心+树状数组/线段树优化区间和)

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

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 1103  Solved: 802
[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

HINT

 


 

发现1:两个相同数之间最少要移动 出现次数为单数的数字的个数 次才能相消,于是想到贪心,sort两个数之间至少移动次数(即前述的个数),按这个顺序消。

发现2:不用sort,先消后消按什么顺序消并不影响,只要我们把最少移动次数=出现次数为单数的数字的个数,所以从左往右扫,树状数组维护一个区间和即可。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cctype>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<vector>
#include<stack>
#include<cmath>
#include<bits/stdc++.h>
#define ri register int
#define ll long long
#define For(i,l,r) for(ri i=l;i<=r;i++)
#define Dfor(i,r,l) for(ri i=r;i>=l;i--)
using namespace std;
const int M=5e4+5;
int n,a[M<<1],l[M<<1],t[M<<1],ans;
inline ll read(){
    ll f=1,sum=0;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){sum=(sum<<1)+(sum<<3)+(ch^48);ch=getchar();}
    return f*sum;
}
inline int query(int x){
    int sum=0;
    for(ri i=x;i;i-=i&(-i)){
        sum+=t[i];
    }
    return sum;
}
inline void add(int x,int val){
    for(ri i=x;i<=n<<1;i+=i&(-i)){
        t[i]+=val;
    }
}
int main(){
    n=read();
    For(i,1,n<<1) a[i]=read();
    For(i,1,n<<1){
        if(!l[a[i]]){
            add(i,1);l[a[i]]=i;
        }
        else{
            ans+=query(i)-query(l[a[i]]);
            add(l[a[i]],-1);
        }
    }
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/jian-song/p/11774057.html