题目大意:

有一排acm大牛(20,000),告诉你他们每个人的听力水平和所在位置坐标,并且他们每个人之间交流都需要的声音大小为:他们之间的距离乘他们两个人听力水平的较大值。现在问你每一对牛都交流一次,并且他们交流都是用的能交流的最小的声音,请算出他们这个活动产生的各种声音的大小的总和(应该是相同大小的声音多次出现分别都加上)。

分析:

至少要求出每两个牛交流产生的声音值 20000(20001)/2=2108 ,这样就会产生这么多的值,然后用一个数组标记是否出现过,时间 2108 。这大概就是传说中的超时吧。
话说上面的也没用到树状数组啊,这怎么能行。再考虑一个问题,比如所需声音最大的和其他的所有牛交流一次,产生的声音的总和为: v[max](dis[1]+dis[2]+...+dis[n]) (dis [ i ] 表示i距离max的距离)其中 (dis[1]+dis[2]+...+dis[n]) 可以用树状数组求出。
具体的代码实现技巧就是,用两个树状数组,一个记录坐标和,一个记录个数。求一个点右面的和等于树状数组总和减这个点左面的和。

代码:

#include<iostream> 
#include<string.h>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#define maxn 20500
using namespace std;

struct xcx
{
    int x;
    int v;
}cow[maxn];

int n;

long long int tree[3][maxn]={0};//tree[1]计算有多少个,tree[2]计算坐标和是多少 

bool my_cmp(xcx x,xcx y)
{
    return x.v<y.v;
}

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

void add(int x)//加上坐标为x的一个点 
{
    int t=x;
    while(x<=maxn)
    {
        tree[1][x]+=1;
        tree[2][x]+=t;
        x+=lowbit(x);
    }
}

long long int sum(int i,int x)//i=1求个数,i=2求坐标和 
{
    long long int s=0;
    while(x>0)
    {
        s+=tree[i][x];
        x-=lowbit(x);
    }
    return s;
}


int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&cow[i].v,&cow[i].x);
    }
    sort(cow+1,cow+n+1,my_cmp);
    /*for(int i=1;i<=n;i++) { cout<<cow[i].v<<endl; }*/
    long long int best=0;
    for(int i=1;i<=n;i++)
    {
        long long int l,r;
        l=sum(1,cow[i].x)*cow[i].x-sum(2,cow[i].x);
        r=sum(2,maxn)-sum(2,cow[i].x)-(sum(1,maxn)-sum(1,cow[i].x))*cow[i].x;
        best+=cow[i].v*(l+r);
        //cout<<l<<" "<<r<<" "<<best<<endl;
        add(cow[i].x);
    }
    cout<<best<<endl;
}

后注:

这次完全自己思路,一次ac,爽~
注意回来要仔细研究一下离线处理的思想。