1. 程式人生 > >[USACO] 2004 Open MooFest 奶牛集會

[USACO] 2004 Open MooFest 奶牛集會

omr isp href check 說明 歸並 sub printf com

題目背景

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\n",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\n",ans);
    return 0;
}

博主蒟蒻,隨意轉載.但必須附上原文鏈接

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

[USACO] 2004 Open MooFest 奶牛集會