樹狀陣列相關應用之二元變數結構體組隊問題
一維陣列處理組隊問題
此類問題的處理方法一般採用定一議二
POJ—1900:Moofest
思路: 樹狀陣列
分析:
1 題目給定n頭牛的聽力v[i]. 現在規定兩頭你i和j如果要進行交流的話那麼消耗的能量就是dis(i,j)max(v[i].v[j]),現在問n頭牛總共的n(n-1)*2種方式消耗的總的能量
2 題目要求的是所有的牛的交流方式的總的消耗能量
看這個樣例
3 1
2 5
2 6
4 3
那麼所有的區間為[1.3],[1,5],[1,6],[3,5],[3,6],[5,6]
那麼總和為4dis[1.3]+3dis[1,5]+3dis[1,6]+4
那麼題目要求的ans = ∑(v[i]*(所有比v[i]小的牛的座標的總和))
3 那麼我們來分解這個式子,我們對點按照音量的值從小到大排完序之後
那麼對於任一的一點i,i之前的牛的音量值肯定小於v[i],但是座標的值可能比x[i]大也可能比x[i]小,因此我們應該分成兩部分來考慮,就是座標是i的左邊和右邊
首先考慮左邊的情況,假設左邊比小於等於v[i]的牛有三頭座標分別為a b c,那麼左邊的值就是v[i](x[i]-a)+v[i]
那麼我們假設左邊小於v[i]的牛有countLeft頭,總的座標為totalLeft,那麼左邊的值為v[i](countLeftx[i]-totalLeft);
接下來考慮右邊的情況,由於我們已經按照v的值排序,那麼我們能夠很好的計算出小於等於v[i]的音量值的總的座標之後,我們設為totalDis,那麼根據左邊求出的小於等於v[i]的個數為countLeft,那麼右邊的個數為i-countLeft,那麼同理右邊的座標之和為totalDis-totalLeft , 那麼右邊的值為v[i]*(totalDis-totalLeft-(i-countLeft)*x[i]);
那麼對於排序後的第i頭牛來說比它小的音量的牛的值為v[i](countLeftx[i]-totalLeft)+v[i]*(totalDis-totalLeft-(i-countLeft)*x[i]);
4 我們已經知道了公式,現在我們只要去求countLeft和totalLeft即可,由於我們已經按照v的值排序, 那麼我們只要對座標建立兩個樹狀陣列即可。一個用來儲存個數,一個用來儲存座標之和,那麼對於第i頭牛來說我們就能夠在O(logn)的時間內求出countLeft和totalLeft
#include <iostream>
#include <cstring>
#include <stdio.h>
#include <algorithm>
#define MAXSIZE 20005
using namespace std; //使用名稱空間std
struct cow
{
long long vi, xi; //聽力值和位置
};
long long val_sum_x[MAXSIZE]; //更新座標值
long long val_x[MAXSIZE]; //更新位置
cow arry[MAXSIZE];
int main()
{
bool cmp(cow a, cow b);
int lowbit(int x);
void update_1(long long val[], long long i, long long cal, int arry_num);
long long sum_pre_1(long long val[], int i);
long long sum_between_1(long long val[], int pre, int last);
int N;
int left_x, right_x;
long long left_sum, right_sum;
long long sum_v;
while (~scanf("%d", &N))
{
memset(val_x, 0, sizeof(val_x));
memset(val_sum_x, 0, sizeof(val_sum_x));
memset(arry, 0, sizeof(long long) * 2 * MAXSIZE);
sum_v = 0;
for (int i = 1;i <= N;i++)
scanf("%lld%lld", &arry[i].vi, &arry[i].xi);
sort(arry + 1, arry + 1 + N, cmp);
for (int i = 1;i <= N;i++)
{
update_1(val_x, arry[i].xi, 1, MAXSIZE);
update_1(val_sum_x, arry[i].xi, arry[i].xi, MAXSIZE);
left_x = sum_between_1(val_x, 1, arry[i].xi - 1);
right_x = sum_between_1(val_x, arry[i].xi + 1, MAXSIZE);
left_sum = sum_between_1(val_sum_x, 1, arry[i].xi - 1);
right_sum = sum_between_1(val_sum_x, arry[i].xi + 1, MAXSIZE);
sum_v = sum_v + arry[i].vi*(left_x*arry[i].xi - left_sum) + arry[i].vi*(right_sum - right_x * arry[i].xi);
}
printf("%lld\n", sum_v);
}
return 0;
}
bool cmp(cow a, cow b)
{
return a.vi < b.vi;
}
int lowbit(int x)
{//返回二進位制數最低位的1對應的數值
return x & (-x); //與運算
}
//一維樹狀陣列
void update_1(long long val[], long long i, long long cal, int arry_num)
{//原陣列第i個元素加上cal,更新樹狀陣列相關元素,arry_num為原陣列的長度
//可直接用於樹狀陣列的建立
for (;i <= arry_num;i += lowbit(i))
val[i] += cal;
}
long long sum_pre_1(long long val[], int i)
{//求arry陣列的前i項和
//val為樹狀陣列地址
long long sum = 0;
for (;i > 0;i -= lowbit(i)) //從後向前每次跳一個lowbit
sum += val[i];
return sum;
}
long long sum_between_1(long long val[], int pre, int last)
{//求原陣列arry在區間[pre-last]的和
return sum_pre_1(val, last) - sum_pre_1(val, pre - 1);
}
組隊問題+資料離散化處理
hdu—3015:Disharmony Tree
思路與上題基本一致,只不過加上了資料離散化處理
#include <iostream>
#include <cstring>
#include <stdio.h>
#include <algorithm>
#define MAXSIZE 100000
using namespace std; //使用名稱空間std
struct Tree
{
long long xi, hi; //x座標位置和高度
};
Tree tree[MAXSIZE]; //原陣列
long long val_x[MAXSIZE]; //樹狀陣列
long long val_num[MAXSIZE]; //計數樹狀陣列
int main()
{
bool cmp_x(Tree a, Tree b);
bool cmp_h_max(Tree a, Tree b);
bool cmp_h(Tree a, Tree b);
int lowbit(int x);
void update_1(long long val[], long long i, long long cal, int arry_num);
long long sum_pre_1(long long val[], int i);
long long sum_between_1(long long val[], int pre, int last);
int N;
long long sum; //計數器
long long temp; //標記記錄器
while (~scanf("%d", &N))
{
sum = 0;
memset(tree, 0, sizeof(tree));
memset(val_x, 0, sizeof(val_x));
memset(val_num, 0, sizeof(val_num));
for (int i = 1;i <= N;i++)
scanf("%lld%lld", &tree[i].xi, &tree[i].hi);
//輸入完畢
temp = 1;
sort(tree + 1, tree + 1 + N, cmp_x); //降序處理資料離散化
for (int i = 1;i <= N;i++)
{
if (tree[i].xi == tree[i+1].xi)
tree[i].xi = temp;
else
{
tree[i].xi = temp;
temp=i+1;
}
}
//x資料離散化完畢
temp = 1;
sort(tree + 1, tree + 1 + N, cmp_h); //降序處理資料離散化
for (int i = 1;i <= N;i++)
{
if (tree[i].hi == tree[i+1].hi)
tree[i].hi = temp;
else
{
tree[i].hi = temp;
temp=i+1;
}
}
//h資料離散化完畢
sort(tree + 1, tree + 1 + N, cmp_h_max); //升序處理組隊問題
long long left_x, left_num, right_x, right_num;
for (int i = 1;i <= N;i++)
{
update_1(val_x, tree[i].xi, tree[i].xi, N);
update_1(val_num, tree[i].xi, 1, N);
left_x = sum_between_1(val_x, 1, tree[i].xi - 1);
right_x = sum_between_1(val_x, tree[i].xi + 1, N);
left_num = sum_between_1(val_num, 1, tree[i].xi - 1);
right_num = sum_between_1(val_num, tree[i].xi + 1, N);
sum = sum + tree[i].hi*(tree[i].xi*left_num - left_x) + tree[i].hi*(right_x - tree[i].xi*right_num);
}
printf("%lld\n", sum);
}
return 0;
}
bool cmp_x(Tree a, Tree b)
{//降序——>離散化資料
return a.xi < b.xi;
}
bool cmp_h_max(Tree a, Tree b)
{//升序——>解決組對問題
return a.hi > b.hi;
}
bool cmp_h(Tree a, Tree b)
{//降序——>離散化資料
return a.hi < b.hi;
}
int lowbit(int x)
{//返回二進位制數最低位的1對應的數值
return x & (-x); //與運算
}
//一維樹狀陣列
void update_1(long long val[], long long i, long long cal, int arry_num)
{//原陣列第i個元素加上cal,更新樹狀陣列相關元素,arry_num為原陣列的長度
//可直接用於樹狀陣列的建立
for (;i <= arry_num;i += lowbit(i))
val[i] += cal;
}
long long sum_pre_1(long long val[], int i)
{//求arry陣列的前i項和
//val為樹狀陣列地址
long long sum = 0;
for (;i > 0;i -= lowbit(i)) //從後向前每次跳一個lowbit
sum += val[i];
return sum;
}
long long sum_between_1(long long val[], int pre, int last)
{//求原陣列arry在區間[pre-last]的和
return sum_pre_1(val, last) - sum_pre_1(val, pre - 1);
}