1. 程式人生 > >樹狀陣列相關應用之二元變數結構體組隊問題

樹狀陣列相關應用之二元變數結構體組隊問題

一維陣列處理組隊問題

此類問題的處理方法一般採用定一議二
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

dis[3,5]+4dis[3,6]+2dis[5,6] = 4*(dis[1.3]+dis[3,5]+dis[3,6])+3*(dis[1,5]+dis[1,6])+2*(dis[5,6]);

那麼題目要求的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]

(x[i]-b)+v[i](x[i]-c) => v[i](3*x[i]-(a+b+c))

那麼我們假設左邊小於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);
}