您的位置:首页 > 其它

poj1990

2015-11-09 16:15 316 查看
题目意思就是有m头牛,每头牛有两个值,v和x,两两之间有一个值,设v分别为v1,v2,x为x1,x2,则它们之间的值为abs(x1-x2) * Max(v1,v2),求所有m*(m-1)/2对牛之间值的总和。

暴力的话很好理解了,但是这里的m数据范围最大为20000,n^2的效率是不可取的。所以这里我们引入树状数组。这道题目我们得用两个两个树状数组,一个叫做num[x],可以计算小于坐标x的坐标有几个,另外一个叫做sum[x],可以将计算小于坐标x的坐标的总和。除此之外,我们还用一个变量total,来保存前面计算机过的坐标总和。

计算方法是这样的,我们拿例子来说,

4

3 1

2 5

2 6

4 3

首先,我们得将它们按照v从小到大排序,这样子每到一头牛,那么只要计算它与之前的坐标差,然后乘上它的v即可。排序后得到:

2 2 3 4

5 6 1 3

我们先计算5这个,发现它的前面没有坐标,那么就没有好计算的,total+=5,更新num和sum。[total=5]

然后计算6,发现它前面有个坐标5,它们之间的坐标差为6*1-5=1,后面暂时没有坐标,ans = ans+1*2,total+=6,更新num和sum。[total=11]

接着计算1,发现它前面没有坐标,但是此时i=3,说明它的后面有3-0-1个坐标,坐标差为 total-1*2 = 9, ans = ans + 9*3,total += 1,更新num和sum。[total=12].

最后计算3,发现它前面有个坐标1,它们之间坐标差为3*1-1=2,后面有4-1-1=2个坐标,坐标差为 total-1-2*3 = 11-6=5,所以坐标差总共为2+5=7,

ans = ans + 7*4 = ans+28,total += 3,更新num和sum。[total = 15]

最后的ans得出就是57。

更具体的详见代码:

#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<time.h>
#include<queue>
#include<stack>
#include<iterator>
#include<math.h>
#include<stdlib.h>
#include<limits.h>
#include<map>
#include<set>
#include<bitset>
//#define ONLINE_JUDGE
#define eps 1e-8
#define INF 0x7fffffff
#define FOR(i,a) for((i)=0;i<(a);(i)++)
#define MEM(a) (memset((a),0,sizeof(a)))
#define sfs(a) scanf("%s",a)
#define sf(a) scanf("%d",&a)
#define sfI(a) scanf("%I64d",&a)
#define pf(a) printf("%d\n",a)
#define pfI(a) printf("%I64d\n",a)
#define pfs(a) printf("%s\n",a)
#define sfd(a,b) scanf("%d%d",&a,&b)
#define sft(a,b,num) scanf("%d%d%d",&a,&b,&num)
#define for1(i,a,b) for(int i=(a);i<b;i++)
#define for2(i,a,b) for(int i=(a);i<=b;i++)
#define for3(i,a,b)for(int i=(b);i>=a;i--)
#define MEM1(a) memset(a,0,sizeof(a))
#define MEM2(a) memset(a,-1,sizeof(a))
#define ll long long
const double PI=acos(-1.0);
template<class T> T gcd(T a,T b){return b?gcd(b,a%b):a;}
template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;}
template<class T> inline T Min(T a,T b){return a<b?a:b;}
template<class T> inline T Max(T a,T b){return a>b?a:b;}
using namespace std;
#define M 20010
ll num[M];      //num[x]用来存储在[1,x]之间存储的坐标的个数
ll sum[M];      //sum[x]用来存储在[1,x]之间存储的坐标的和
int n;
int lowbit(int x){
return x&(-x);
}
struct Cow{
int val,x;
bool operator <(const Cow &a) const{
return val<a.val;
}
}a[M];
ll getSum(ll *arr,int i){               //树状数组向下求和
ll sum=0;
while(i>0){
sum += arr[i];
i -= lowbit(i);
}
return sum;
}
void upDate(ll *arr,int i,int val){     //树状数组向上更新
while(i<=n){
arr[i] += (ll)val;
i += lowbit(i);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
#endif
int m;
while(sf(m)!=EOF){
memset(num,0,sizeof num);
memset(sum,0,sizeof sum);
n = -1;
for(int i=1;i<=m;i++){
scanf("%d%d",&a[i].val,&a[i].x);
n = Max(n,a[i].x);          //n为最大的坐标,也就是树状数组的右边界
}
sort(a+1,a+m+1);        //依volume从小到大排序
ll total=0;     //记录前i-1头牛所有的volume值的和
ll ans=0;
for(int i=1;i<=m;i++){
ll nm = getSum(num,a[i].x); //计算小于a[i].x的坐标的个数
ll sm = getSum(sum,a[i].x); //计算小于a[i].x的坐标的和
ll small = nm*a[i].x-sm;    //计算小于a[i].x的坐标与a[i].x之间的坐标差
ll big = total-sm-(i-1-nm)*a[i].x;  //计算大于a[i].x的坐标与a[i].x之间的坐标差,
//其中,total-sm是大于a[i].x的vol和,(i-1-nm)是大于a[i].x的坐标的个数
ll val = (small+big)*a[i].val; //计算所有小于等于a[i].val的坐标差*a[i].val
//pfI(val);
ans += val;
total += a[i].x;    //更新total
upDate(num,a[i].x,1);   //更新坐标个数
upDate(sum,a[i].x,a[i].x);  //更新坐标的和
}
pfI(ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: