luoguP5094 [USACO04OPEN]MooFest 狂欢节

get 到的

这种需要求 含 max 的式子枚举最大值的方法非常普遍。

类似的,还有含 min , gcd 的式子,枚举他们也很普遍

主要难点

我们首先想到,先按 v 从小到大排序,因为这样既可以简化题意(即题意:每次都只需求第i头牛 之前的牛的坐标与x[i]差的绝对值 的和(设为s_ans),最后乘以个v[i]即可,(这个应该就是枚举max了) ); 又可以避免重复计算距离

思考难点(即简化后的题意): 已知是考树状数组的题目,又知它方便于求和。

于是...我们点开了题解...

定义:第i头牛前面有num头牛的x坐标比x[i]小,并且他们的和为sum

思考得s_ans =i头牛前面的,xx[i]的牛对答案的贡献s1 +i头牛前面的,xx[i]的牛对答案的贡献s2

s1 = num * a[i].x - sum;
s2 = all - sum - a[i].x * (i-1-num);

#include<cstdio>
#include<algorithm>
using namespace std;
#define lowbit(x) x&(-x)
const int MAX = 20000+9;

int n;
long long ans;
int t_n[MAX]/*num*/, t_sum[MAX];
struct node{
    int x, v;
    bool operator < (const node& xx) const {
        return v < xx.v;
    }
}a[MAX];

void add(int x, int k, int *t) {
    while(x <= MAX) t[x] += k, x += lowbit(x);//注意:坐标求和的右边界不是n 
}
int query(int x, int *t) {
    int res = 0;
    while(x) {res += t[x], x -= lowbit(x);}
    return res;
}

int main() {
  freopen("01.in","r",stdin);
  freopen("01.out","w",stdout);
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d%d",&a[i].v, &a[i].x);
    sort(a+1, a+1+n);
    int all = 0, num, sum;
    for(int i = 1; i <= n; i++) {
        num = query(a[i].x, t_n);//先query再add,因为求的是i牛前面的 
        sum = query(a[i].x, t_sum);
        ans += (long long)( num*a[i].x-sum + all-sum-a[i].x*(i-1-num) )*a[i].v;
        add(a[i].x, 1, t_n);
        add(a[i].x, a[i].x, t_sum);//?
        all += a[i].x;
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/tyner/p/11594566.html