zoukankan      html  css  js  c++  java
  • [USACO] 2004 Open MooFest 奶牛集会

    题目背景

    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)

    Solution

    首先本着打暴力的心情先打了个(O(N^2)),结果数组开小了,然后改了之后就A了???

    Code((N^2))

    #include<bits/stdc++.h>
    #define lol long long
    #define Min(a,b) (a)<(b)?(a):(b)
    #define Max(a,b) (a)>(b)?(a):(b)
      
    using namespace std;
      
    const int N=2e5+10;
      
    void in(int &ans)
    {
        ans=0;int f=1; char i=getchar();
        while(i<'0'||i>'9'){if(i=='-') f=-1; i=getchar();}
        while(i>='0'&&i<='9') ans=(ans<<3)+(ans<<1)+i-'0',i=getchar();
        ans*=f;
    }
      
    int n,m;
    lol ans;
    int v[N],p[N];
    
    int main()
    {
        in(n);
        for(int i=1;i<=n;i++) in(v[i]),in(p[i]);
        for(int i=1;i<=n;i++) {
        for(int j=i+1;j<=n;j++)
            ans+=(Max(v[i],v[j]))*(abs(p[i]-p[j]));
        }
        printf("%lld
    ",ans);
        return 0;
    }
    

    然后想正解,只有当一头奶牛的比其他奶牛大时才有贡献,所以我们把数据按照听力排序之后就可以消除这一点,即当前奶牛不会对后面的奶牛造成影响,因为它的听力没有后面的奶牛好

    那么肯定是需要(O(N))来枚举奶牛的,怎么快速算出它的贡献呢?

    设num[i]为x[i]及x[i]前面的奶牛的个数,sum[i]为x[i]前面奶牛的坐标之和

    都是前缀和

    用一个(log n)的数据结构来维护,树状数组(or)线段树,复杂度(O(N*logN))

    注意:以下说的的之前都指的是在一头奶牛的坐标之前,而不是序号之前

    所以每次扫到一头奶牛之后,它对答案的贡献就是

    (这头奶牛之前的奶牛的数量(*)本头奶牛的坐标-这头奶牛之前的坐标的前缀和)(*) (v[i])+(这头奶牛之后的坐标的前缀和-这头奶牛之后的奶牛的数量(*)本头奶牛的坐标)(*) (v[i])

    也就是

    [((num[i-1]*x[i]-sum[i-1])+(sum[maxn]-sum[i])-(num[maxn]-num[i])*x[i])*v[i] ]

    因为我们是一边枚举一边统计,其中maxn为坐标的最大值,用sum[maxn]和num[maxn]来统计当前有多少头奶牛

    先统计再插入

    这里摆上树状数组的代码

    Code

    #include<bits/stdc++.h>
    #define lol long long
    #define Min(a,b) (a)<(b)?(a):(b)
    #define Max(a,b) (a)>(b)?(a):(b)
     
    using namespace std;
     
    const int N=2e5+10;
     
    void in(int &ans)
    {
        ans=0;int f=1; char i=getchar();
        while(i<'0'||i>'9'){if(i=='-') f=-1; i=getchar();}
        while(i>='0'&&i<='9') ans=(ans<<3)+(ans<<1)+i-'0',i=getchar();
        ans*=f;
    }
     
    int n,m,maxn;
    lol ans;
    struct node {
        int v,x;
        bool operator < (const node & a) const {
    	if(a.v==v) return x<a.x;
    	return v<a.v;
        }
    }sub[N];
    lol num[N],sum[N];
     
    inline int lowbit(int x)
    {
        return x&-x;
    }
    
    void add(int x,int a,lol *f) {
        while(x<=maxn) {
    	f[x]+=a;
    	x+=lowbit(x);
        }
    }
    
    lol check(int x,lol *f) {
        lol ans=0;
        while(x) {
    	ans+=f[x];
    	x-=lowbit(x);
        }
        return ans;
    }
    
    int main()
    {
        in(n);
        for(int i=1;i<=n;i++) in(sub[i].v),in(sub[i].x),maxn=Max(maxn,sub[i].x);
        sort(sub+1,sub+1+n);
        for(int i=1;i<=n;i++) {
    	lol size,dist;
    	
    	size=check(sub[i].x-1,num);
    	dist=check(sub[i].x-1,sum);
    	ans+=sub[i].v*(size*sub[i].x-dist);
    	
    	size=check(maxn,num)-check(sub[i].x,num);
    	dist=check(maxn,sum)-check(sub[i].x,sum);
    	ans+=sub[i].v*(dist-size*sub[i].x);
    	
    	add(sub[i].x,sub[i].x,sum);
    	add(sub[i].x,1,num);
        }
        printf("%lld
    ",ans);
        return 0;
    }
    

    博主蒟蒻,随意转载.但必须附上原文链接

    http://www.cnblogs.com/real-l/

  • 相关阅读:
    c#随便写写 数据层和表现层,队列执行
    unity代码设置鼠标样式
    Unity c#反射查找类中符合条件的方法并执行
    封装你的协程Unity TaskManager
    基于steamworks获取steam用户头像
    开启Unity项目中VS工程的属性面板
    移动端的动态阴影
    c#静态扩展方法,字典的克隆扩展方法
    Unity热更方案汇总
    lua输入函数名字符串执行函数
  • 原文地址:https://www.cnblogs.com/real-l/p/9577421.html
Copyright © 2011-2022 走看看