奶牛狂欢节(dp+线段树优化)

Description
  n头奶牛站成一条直线(编号为1..n),第i头奶牛的位置用坐标x[i]表示,奶牛i,j之间的距离定义为dist(i,j)=|x[i]-x[j]|。第i头奶牛的听力值为v[i],那么奶牛i,j之间要相互交谈,她们的声音不能小于MAX(v[i],v[j])*dist(i,j)。

  现在每对奶牛都在交谈,并且使用最小的音量,那么所有n(n-1)/2头奶牛间谈话的音量之和为多少?


Input
  第1行一个整数n,表示有n头奶牛。接下来的n行,每行表示一头奶牛的信息v[i]和x[i]。


Output

  一行一个整数,表示最后的答案。


分析:

        设一个结构体:{x,v},x表示位置,v表示声音。按照v从大到小排序。

        奶牛1到奶牛2,3,4……n的音量和为:v[1]*sum| x[1]-x[i] | 。奶牛2-n同理。

        设n1为x小于x[i]的数量,n2为x大于x[i]的数量,sum1为x小于x[1]的x之和,sum2为大于的和,那么上式就是:v[1]*(n1*x[1]-sum1)+v[1]*(sum2-n2*x[1]) ;

        至此可以明确:建立权值线段树,维护两个数据:num[i]表示i代表的区间里有多少个数,sum[i]表示i代表的区间里x的和。

        每个奶牛算完后,把这头奶牛删掉。


#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int INF=200005;
const int maxn=50005;
int n,rt=0,np=0,lc[INF*2],rc[INF*2],num[INF*2];
LL sum[INF*2];
struct data{int v,x;}cow[maxn];
bool cmp(data a,data b) {return a.v>b.v;}

void build(int &now,int L,int R)
{
	now=++np;
	if(L==R) return;
	int m=(L+R)/2;
	build(lc[now],L,m);
	build(rc[now],m+1,R);
}

void pushup(int now,int L,int R)
{
	num[now]=num[L]+num[R];
	sum[now]=sum[L]+sum[R];
}

void update(int now,int L,int R,int x,int d)
{
	if(L==R)//L=R=x
	{
		num[now]+=d;
		sum[now]+=x*d;
		return;
	}
	int m=(L+R)/2;
	if(x<=m) update(lc[now],L,m,x,d);
	else update(rc[now],m+1,R,x,d);
	pushup(now,lc[now],rc[now]);
}

int Count1(int now,int L,int R,int x)
{
	if(L==R) return 0;
	int m=(L+R)/2,t=num[lc[now]];
	if(x<=m) return Count1(lc[now],L,m,x);
	return t+Count1(rc[now],m+1,R,x);
}

int Count2(int now,int L,int R,int x)
{
	if(L==R) return 0;
	int m=(L+R)/2,t=num[rc[now]];
	if(x<=m) return t+Count2(lc[now],L,m,x);
	return Count2(rc[now],m+1,R,x);
}

int Kth(int now,int L,int R,int k)
{
	if(L==R) return L;
	int m=(L+R)/2,t=num[lc[now]];
	if(k<=t) return Kth(lc[now],L,m,k);
	return Kth(rc[now],m+1,R,k-t);
}

LL Sum(int now,int L,int R,int i,int j)
{
	if(i<=L&&j>=R) return sum[now];
	int m=(L+R)/2;
	LL ans=0;
	if(i<=m) ans+=Sum(lc[now],L,m,i,j);
	if(j>m) ans+=Sum(rc[now],m+1,R,i,j);
	return ans;
}

int main()
{
//	freopen("in.txt","r",stdin);
	scanf("%d",&n);
	build(rt,0,INF);
	for(int i=1;i<=n;i++) 
	{
		scanf("%d%d",&cow[i].v,&cow[i].x);
		update(rt,0,INF,cow[i].x,1);//每头牛的x加入集合 
	}
	sort(cow+1,cow+1+n,cmp);//按照v排序 
	LL ans=0;
	for(int i=1;i<=n;i++)
	{
		int v=cow[i].v;
		int n1=Count1(rt,0,INF,cow[i].x);//小于x[i]的个数 
		int n2=Count2(rt,0,INF,cow[i].x);//大于x[i]的个数 
		int k1=Kth(rt,0,INF,n1);//恰好小于x[i]的数 
		int k2=Kth(rt,0,INF,n1+1+n2);//恰好大于x[i]的数 
		LL sum1=Sum(rt,0,INF,0,k1);//x[i]之前的x之和 
		LL sum2=Sum(rt,0,INF,cow[i].x+1,k2);//x[i]之后的x之和 
		ans+=(LL)v*((LL)n1*cow[i].x-sum1+sum2-(LL)n2*cow[i].x);//统计答案 
		update(rt,0,INF,cow[i].x,-1);//删掉这头奶牛 
	}
	printf("%lld",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/wwwengine/article/details/80978837