POJ 1990 MooFest(树状数组 标记计数应用)

题意:

            N头牛,每个有两个属性:距离和权值,任意两个都可以交流,两头牛交流的花费是其中权值较大的权值。问所有的牛可以两两交流,共需要花费多少权值。

 

题解:

            按照V从小到大排序,当前处理的就是最大的。然后怎么去找在他之前的牛的d和在它之后的d呢?在它前面的d总和可以用树状数组标记法去求,在它之后的只需用当前处理的总距离减去前面的距离总和,就可以得到在它后面的距离总和。

开两个树状数组,第一个记录在牛x之前的数量,另一个记录在它之前的坐标总和。

 

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int maxn = 20111;

long long c[2][maxn];

struct node
{
    int v,d;
    bool operator <(const node& a)const
    {
        return v<a.v;
    }
}ns[maxn];


void add(int i,int x,int v)
{
    while(x<maxn)
    {
        c[i][x]+=v;
        x+=(x & -x);
    }
}

long long sum(int i,int x)
{
    long long res=0;
    while(x>0)
    {
        res+=c[i][x];
        x-=(x&-x);
    }
    return res;
}


int main()
{
    int n;
    while(cin>>n)
    {
        memset(c,0,sizeof c);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&ns[i].v,&ns[i].d);
        }
        sort(ns+1,ns+1+n);
        long long ans=0;
        long long tot=0;//坐标和
        for(int i=1;i<=n;i++)
        {   int x=ns[i].d;
            tot+=x;///总的坐标和
            int s1=sum(0,x);//i之前有多少头
            long long s2=sum(1,x);//i之前坐标和
            long long q=s1*x-s2;//左边的坐标差

            long long w=tot-x-s2-x*(i-1-s1);//右边的坐标差
            ans+=(q+w)*ns[i].v;
            add(0,x,1);
            add(1,x,x);
        }
        cout<<ans<<endl;
    }
}

 

 

猜你喜欢

转载自blog.csdn.net/sgh666666/article/details/80446574