【洛谷P2345】奶牛集会
题目背景
MooFest, 2004 Open
题目描述
约翰的N 头奶牛每年都会参加“哞哞大会”。哞哞大会是奶牛界的盛事。集会上的活动很
多,比如堆干草,跨栅栏,摸牛仔的屁股等等。它们参加活动时会聚在一起,第i 头奶牛的坐标为Xi,没有两头奶牛的坐标是相同的。奶牛们的叫声很大,第i 头和第j 头奶牛交流,会发出max{Vi; Vj}×|Xi − Xj | 的音量,其中Vi 和Vj 分别是第i 头和第j 头奶牛的听力。假设每对奶牛之间同时都在说话,请计算所有奶牛产生的音量之和是多少。
输入输出格式
输入格式:
• 第一行:单个整数N,1 ≤ N ≤ 20000
• 第二行到第N + 1 行:第i + 1 行有两个整数Vi 和Xi,1 ≤ Vi ≤ 20000; 1 ≤ Xi ≤ 20000
输出格式:
• 单个整数:表示所有奶牛产生的音量之和
输入输出样例
输入样例#1:
4
3 1
2 5
2 6
4 3
输出样例#1:
57
说明
朴素O(N2)
类似于归并排序的二分O(N logN)
树状数组O(N logN)
知识点
树状数组
分析
朴素算法O(N2)能过去一部分点。
题目上给的公式max{Vi; Vj}×|Xi − Xj |。
我们可以先按照V进行从小到大排序,这样就可以排除V的干扰,也就是保证当前的Vi是最大的。
就可以用树状数组计算前缀和了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=20000+5;
typedef long long ll;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
int n;
int c1[maxn],c2[maxn];
int mx,mn;
struct node
{
int v,x;
bool operator < (const node &j) const {
return v<j.v;
}
}e[maxn];
void update1(int x,int d)
{while(x<=mx){c1[x]+=d;x+=x&(-x);}}
void update2(int x,int d)
{while(x<=mx){c2[x]+=d;x+=x&(-x);}}
ll ask1(int x)
{ll ans=0;while(x){ans+=c1[x];x-=x&(-x);}return ans;}
ll ask2(int x)
{ll ans=0;while(x){ans+=c2[x];x-=x&(-x);}return ans;}
int main()
{
n=read();
for(int i=1;i<=n;i++)
{
e[i].v=read();e[i].x=read();
mx=max(mx,e[i].x);
}
sort(e+1,e+n+1);
ll ans=0;
for(int i=1;i<=n;i++)
{
int x=e[i].x,v=e[i].v;
ll num1,num2,sum1,sum2;
num1=ask1(x-1);sum1=ask2(x-1);
num2=ask1(mx)-ask1(x);
sum2=ask2(mx)-ask2(x);
ans+=v*(sum2-x*num2+x*num1-sum1);
update1(x,1);
update2(x,x);
}
printf("%lld\n",ans);
return 0;
}
欢迎转载,转载请注明出处!

浙公网安备 33010602011771号