Luogu P2345 奶牛集会

传送门

这道题可以用分治的方法解决。(lbgxld说是线段树,但是我觉得分治快而且好写...)

暴力枚举是$O(n^2)$,想要优化,就不能一对一对枚举,最好能用一只奶牛一次计算一群其他的贡献。

这就需要用到听力$v$和坐标大小$x$的关系,可以把它转化为一个二维偏序问题。

首先把听力$v$从大到小排序,控制一维。

然后用归并排序,按$x$从小到大排序。

因为左边一半$(l-mid)$奶牛的$v$一定大于右边的$(mid+1-r)$,那么我们只计算左边的每一个对右边的贡献。

首先枚举统计出右边奶牛坐标的和。

对于左边的一个奶牛,设它的听力为$vi$,坐标为$xi$。

两边已经按$x$从小到大排好,容易统计出有几只奶牛的坐标比它小,而剩余的比它大。

要求坐标差的绝对值,那么分别计算($xi$-小)+(大-$xi$),最后乘这个左边的奶牛的$vi$(因为这个$vi$一定大于所有右边的)。

代码如下

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define MogeKo qwq
#include<algorithm>
#define int long long
using namespace std;
const int maxn = 1e6;
int n,ans;

struct vx {
  int v,x;
  bool operator < (const vx & N) const {
    return v>N.v || ( v==N.v && x<N.x);
  }
} a[maxn],b[maxn];

void cdq(int l,int r) {
  if(l==r)return;
  int mid = (l+r)>>1;
  cdq(l,mid);
  cdq(mid+1,r);
  int s1 = 0,s2 = 0;
  for(int i = mid+1;i <= r;i++)
    s2 += a[i].x;
  int i = l,j = mid+1,k = l;
  while(i <= mid) {
    while(j <= r && a[i].x > a[j].x) {
      s1 += a[j].x;
      s2 -= a[j].x;
      b[k++] = a[j++];
    }
    ans += a[i].v*((j-mid-1)*a[i].x - s1 + s2 - (r-j+1)*a[i].x);
    b[k++] = a[i++];
  }
  while(j <= r)
    b[k++] = a[j++];
  for(int i = l; i <= r; i++)
    a[i] = b[i];
}

main() {
  scanf("%lld",&n);
  for(int i = 1; i <= n; i++)
    scanf("%lld%lld",&a[i].v,&a[i].x);
  sort(a+1,a+n+1);
  cdq(1,n);
  printf("%lld\n",ans);
}
View Code

猜你喜欢

转载自www.cnblogs.com/mogeko/p/10943622.html