1. 程式人生 > >LA 4329(樹狀數組)

LA 4329(樹狀數組)

tdi string 註意 每一個 方法 long 結果 ios pac

算法競賽入門經典 p197

題目大意:

一條大街上住著n個乒乓球愛好者。常常比賽切磋技術。每一個人都有一個不同的技能值a[i]。每場比賽須要3個人:兩名選手,一名裁判。他們有個奇怪的約定,裁判必須住在兩名選手之間,而裁判的能力值也必須在兩名選手之間。問一共能組織多少種比賽。

分析:

如果a[1]到a[i-1]中小於a[i]的數有p[i]。a[i+1]到a[n]中小於a[i]的數有s[i]個;

這樣當i為裁判時可以組織的比賽數目為:p[i]*(n-i-s[i]) + (i-1-p[i])*s[i];

則總比賽次數為:

ans = 0;
for i -> 1 to n   (i表示選取第i個人作為裁判)
    ans += p[i]*(n-i-s[i]) + (i-1-p[i])*s[i];
首先確定p[i]的值,令x[j]表示到眼下為止已經考慮過的全部a[i]中是否存在技能值為j的數;(x[j] = 0表示不存在,x[j] = 1表示存在)

memsest(x, 0, sizeof(x));(將x初始化為0);
for i -> 1 to cur    (cur為考慮的當前位置,即選取的裁判位置)
    x[a[i]] = 1;

則有 p[cur] = x[1]+x[2]+.....+x[a[cur]-1];

例:

如果 n = 4 a[1] = 2, a[2] = 3, a[3] = 5, a[4] = 1;

技術分享

選取 cur= 3,a[cur] = 5; (第三個人做裁判)

p[3] = x[1]+x[2]+x[3]+x[4] = 0 + 1 + 1 + 0 = 2;(這裏 x[1] = 0的原因是沒有運行到第4個)

不斷的記錄求和,當然是沒有問題的(時間開銷非常大)

for i -> 1 to n;
    x[a[i]] = 1;
    p[i] = 0;
    for j -> 1 to a[i]-1
        p[i] += x[j]

改動單個元素並求前綴和是樹狀數組的標準使用方法,能夠大幅度縮減時間(時間復雜度從O(nr)降到O(nlogr) );

for i-> 1 to n
    add(a[i], 1); //(點改動)
    p[i] = sum(a[i]-1); //(前綴和);
到這裏結果基本上能夠求出來了,那s[i]呢?類似的。方向從i -> 1 to n 改為 i -> n todown 1就可以;
代碼例如以下:

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 20000+10;
const int maxm = 100000+10;
int c[maxm], a[maxn], p[maxn], s[maxn], n;

inline int lowbit(int x){
    return x&-x;
}

void add(int x, int d){
    while(x <= maxm){    // 一定註意這裏是maxm, 原因能夠思考一下;
        c[x] += d; x += lowbit(x);
    }
}

int sum(int x){
    int ret = 0;
    while(x > 0){
        ret += c[x]; x -= lowbit(x);
    }
    return ret;
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--){
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        memset(c, 0, sizeof(c));
        for(int i = 1; i <= n; ++i){
            add(a[i], 1);
            p[i] = sum(a[i]-1);
        }
        memset(c, 0, sizeof(c));
        for(int i = n; i > 0; --i){
            add(a[i], 1);
            s[i] = sum(a[i]-1);
        }
        long long ans = 0;
        for(int i = 1; i <= n; ++i){
            ans += p[i]*(n-i-s[i]) + (i-1-p[i])*s[i];
        }
        printf("%lld\n", ans);
    }
    return 0;
}


LA 4329(樹狀數組)