POJ1990MooFest

题意:牛的听力为v,两头牛i,j之间交流,需要max(v[i], v[j])*dist(i,j)的音量。应该说,题意是说,v[i]越小,听力越好。
求所有牛交谈时叫声总和∑(max(v[i],v[j])*abs(dist[j]-dist[i]) ) )
思路:先对牛按照v从小到大排序。对于牛i,它与比他听力还小的牛之间交谈需要音量都是v[i],再乘以之间的距离就可以了。在排好序后,假设:
count[i]:比i听力小的且x坐标比第i头牛小的牛总数
total[i]:count[i]中那些牛的x坐标总和
alltotal:表示所有比第i头牛听力小的牛的总数的话
那么,原题要求的式子就成了
∑( v[i] * 所有比i听力小的牛到i的总距离 )
=∑( v[i] * (count[i]*x[i] – total + alltotal – total – (i – count[i] – 1) * x[i] ) )
其中,count[i]*x[i] – total表示所有比i听力小且在它左边的牛们到i的距离总和, alltotal – total – (i – count[i] – 1) * x[i] ) 表示所有比i听力小且在它右边的牛们到i的距离总和。这个有点难理解,自己画个图试试看。
剩下的关键就在,如何求count[i]和total[i]更快。因为如果排好序后是扫一遍所有牛的坐标的话,时间复杂度就是n^2了,不行。所以想到了树状数组。树状数组用于动态的求一个数组前i个数的总和。

所以,把count作为一个树状数组,如果在坐标x上有一头牛,那么count[x] = 1。这样求i之前有多少头牛,就是count(i)的一个查询。把total同样作为一个树状数组,如果坐标x上有一头牛,那么total[x] += x。这是因为total数组表示的是距离。这样求i之前听力小于v[i]且在i左边的牛总数,就是total(i)的查询。

count[i]之所以是小于,是因为x坐标唯一

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn = 20005;
long long Count[maxn], Total[maxn];
struct Cow
{
    int vol, x;
}cow[maxn];
bool cmp(Cow p, Cow q)
{
    if(p.vol == q.vol)
        return p.x < q.x;
    return p.vol < q.vol;
}
int lowbit(int lo)
{
    return lo & (-lo);
}
void modify1(int pos, int value)
{
    while(pos < maxn)
    {
        Count[pos] += value;
        pos += lowbit(pos);
    }
}
void modify2(int pos, int value)
{
    while(pos < maxn)
    {
        Total[pos] += value;
        pos += lowbit(pos);
    }
}
long long getsum1(int pos)
{
    long long sum = 0;
    while(pos > 0)
    {
        sum += Count[pos];
        pos -= lowbit(pos);
    }
    return sum;
}
long long getsum2(int pos)
{
    long long sum = 0;
    while(pos > 0)
    {
        sum += Total[pos];
        pos -= lowbit(pos);
    }
    return sum;
}
int main()
{
    int n;
    long long allTotal, ans;
    while(cin >> n)
    {
        allTotal = ans = 0;
        memset(Count, 0, sizeof(Count));
        memset(Total, 0, sizeof(Total));
        for(int i = 1; i <= n; i++)
            scanf("%d%d", &cow[i].vol, &cow[i].x);
        sort(cow + 1, cow + n + 1, cmp);
        for(int i = 1; i <= n; i++)
        {
            int x = cow[i].x;
            ans += cow[i].vol * (getsum1(x)*x - getsum2(x) + allTotal -  getsum2(x) - (i-getsum1(x) - 1)*x );
            //因为x坐标不能重复,所以getsum1(x)取的的肯定是听力小的,而且x坐标比他小的
            modify1(x, 1);
            modify2(x, x);
            allTotal += x;
        }
        cout << ans << endl;
    }
    return 0;
}

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
#define F(i,START,END) for (int i=START;i<=END;i++)
const int maxn = 20005;
struct Cow
{
    int vo, x;
    bool operator < (const Cow &other) const
    {
        if(vo == other.vo)
            return x < other.x;
        return vo < other.vo;
    }
}cow[maxn];
struct treearray
{
    long long C[maxn], n;
    treearray(){}
    treearray(int _n){Clear(), n = _n;}
    void Clear()
    {
        memset(C, 0, sizeof(C));
    }
    int lowbit(int lo)
    {
        return lo & (-lo);
    }
    void modify(int pos, int value)
    {
        while(pos < n)
        {
            C[pos] += value;
            pos += lowbit(pos);
        }
    }
    long long getsum(int pos)
    {
        long long sum = 0;
        while(pos > 0)
        {
            sum += C[pos];
            pos -= lowbit(pos);
        }
        return sum;
    }
}Count, Total;
int main()
{
    int n;
    while(cin >> n)
    {
        long long ans = 0, allTotal = 0;
        F(i, 1, n)
            scanf("%d%d", &cow[i].vo, &cow[i].x);
        sort(cow + 1, cow + n + 1);
        Total = treearray(20002);
        Count = treearray(20002);
        F(i, 1, n)
        {
            int x = cow[i].x;
            ans += cow[i].vo * (Count.getsum(x)*x - Total.getsum(x) + allTotal - Total.getsum(x) - (i - Count.getsum(x) - 1) * x);
            Count.modify(x, 1);
            Total.modify(x, x);
            allTotal += x;
        }
        cout << ans << endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值