hdu2838(树状数组)

Problem Description Sherlock’s N (1 ≤ N ≤ 100,000) cows are lined up
to be milked in the evening. Each cow has a unique “grumpiness” level
in the range 1…100,000. Since grumpy cows are more likely to damage
Sherlock’s milking equipment, Sherlock would like to reorder the cows
in line so they are lined up in increasing order of grumpiness. During
this process, the places of any two cows (necessarily adjacent) can be
interchanged. Since grumpy cows are harder to move, it takes Sherlock
a total of X + Y units of time to exchange two cows whose grumpiness
levels are X and Y.

Please help Sherlock calculate the minimal time required to reorder the cows.

Input Line 1: A single integer: N Lines 2…N + 1: Each line contains a single integer: line i + 1 describes the grumpiness of cow i.
Output Line 1: A single line with the minimal time required to reorder the cows in increasing order of grumpiness.

Sample Input
3
2 3 1

Sample Output
7

题意:
给定n个乱序的数字,排序只能交换相邻的两个数字,交换两个数字的代价是两个数字之和,求排好序后的消耗。
思路:
先注意两个点:n的范围最大100000,而且最后求和的ans可能会超出int。
我们要的是最小的交换次数,即可得到最小的消耗代价。
a[i]是输入的数组话
那么对于a[i]来说,a[i]的最小交换次数就是 [1,i-1 ]区间内 有多少个大于a[i]的数,那么就交换多少次,
而且第i个数排好序后的代价为 交换次数*a[i] + 所有大于a[i]的数字之和(在a[i]的前面)
对每个数字都求一遍就得到答案了。
那么我们令 交换次数为cnt[],sum[]为大于a[i]的数字之和 cnt[],和sum[]都是树状数组,具体分析在代码中

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int cnt[100005];
ll sum[100005];
int n;
int lowbit(int x)
{
    return x&(-x);
}
void update1(int i,int k)
{

    while(i<=n)
    {
        cnt[i]++;
        i+=lowbit(i);
    }
}
//这两个函数可以一起写
void update2(int i,int k)
{

    while(i<=n)
    {
        sum[i]+=k;
        i+=lowbit(i);
    }
}

int getcnt(int x)
{
    int res=0;
    while(x>0)
    {
        res+=cnt[x];
        x-=lowbit(x);
    }
    return res;
}

ll getsum(int x)
{
    ll res=0;
    while(x>0)
    {
        res+=sum[x];
        x-=lowbit(x);
    }
    return res;
}
int main()
{
    int t;
    ll ans;
    while(scanf("%d",&n)==1)
    {
        ans=0;
        memset(cnt,0,sizeof cnt);
        memset(sum,0,sizeof sum);
        for(int i=1;i<=n;i++)
        {
/*
            cnt[]和sum[]都是树状数组,他们原始数组是一个全为1,一个是输入的t,这里省略了

            cnt数组是维护第i个位置的前面有多少个小于等于他的数,用getcnt可以得到
            那么i-getcnt(t)得到的就是第i个数字前面比他大的数字的个数
            也可以是getcnt(n)-getcnt(t),结果是一样的

            sum数组维护的是第i个数字前面,小于等于他的数字的和,getsum即可得到
            那么getsum(n)-getsum(t)得到的就是i前面比i大的数字的和

*/
            scanf("%d",&t);
            update1(t,1);//第t个位置加1,更新的是cnt数组
            update2(t,t);//第t个位置加t,更新的是sum数组
            //存在溢出
            ans+=(ll)t*(i-getcnt(t));
            ans+=getsum(n)-getsum(t);
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值