题目背景
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; }
时间: 2024-10-14 10:31:44