hoj 2430 Counting the algorithms +树状数组

Counting the algorithms

As most of the ACMers, wy's next target is algorithms, too. wy is clever, so he can learn most of the algorithms quickly. After a short time, he has learned a lot. One day, mostleg asked him that how many he had learned. That was really a hard problem, so wy wanted to change to count other things to distract mostleg's attention. The following problem will tell you what wy counted.

Given 2N integers in a line, in which each integer in the range from 1 to N will appear exactly twice. You job is to choose one integer each time and erase the two of its appearances and get a mark calculated by the differece of there position. For example, if the first 3 is in position 86 and the second 3 is in position 88, you can get 2 marks if you choose to erase 3 at this time. You should notice that after one turn of erasing, integers' positions may change, that is, vacant positions (without integer) in front of non-vacant positions is not allowed.

Input

There are multiply test cases. Each test case contains two lines.

The first line: one integer N(1 <= N <= 100000).

The second line: 2N integers. You can assume that each integer in [1,N] will appear just twice.

Output

One line for each test case, the maximum mark you can get.

Sample Input

3
1 2 3 1 2 3
3
1 2 3 3 2 1

Sample Output

6
9
 
 
 
 
题意:给出2*N的序列,每个数∈[1,N]出现2次  2个数之间的间隔为得分,
         求得一个得分后会删除这两个数,问最大得分

经典树状数组的应用,从这道题学到了。值得思考。
 
 
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int M=200005;

int b[M],e[M],s[M],a[M],n;   //用b数组和e数组来维护相同数字的起始位置,a数组存序列的值

int lowbit(int x){
    return x&(-x);
}

void modify(int x,int y){
    for(int i=x;i<=n;i+=lowbit(i))
    s[i]+=y;
}

int sum(int x){
    int ans=0;
    for(int i=x;i>0;i-=lowbit(i))
    ans+=s[i];
    return ans;
}

int main(){
    int ans;

    while(~scanf("%d",&n)){
        n*=2;
        ans=0;
        memset(b,0,sizeof(b));
        memset(e,0,sizeof(e));
        memset(s,0,sizeof(s));
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
            if(b[a[i]]) e[a[i]]=i; //标记起始位置
            else b[a[i]]=i;
            modify(i,1);
        }
        for(int i=n;i>0;--i){
            if(b[a[i]]==0) continue;   //已经清除
            ans+=(sum(i)-sum(b[a[i]]));   //i为末位置即a[i]结束位置,b[]为开始位置
            modify(b[a[i]],-1);           //修改开始和结束位置的值
            modify(i,-1);
            b[a[i]]=0;                 //设为清除
        }
        cout<<ans<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值