划分树 Minimum Sum

Minimum Sum
Time Limit:8000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u

Description

You are given N positive integers, denoted as x0, x1 ... xN-1. Then give you some intervals [l, r]. For each interval, you need to find a number x to make   as small as possible!
 

Input

The first line is an integer T (T <= 10), indicating the number of test cases. For each test case, an integer N (1 <= N <= 100,000) comes first. Then comes N positive integers x (1 <= x <= 1,000, 000,000) in the next line. Finally, comes an integer Q (1 <= Q <= 100,000), indicting there are Q queries. Each query consists of two integers l, r (0 <= l <= r < N), meaning the interval you should deal with.

 

Output

For the k-th test case, first output “Case #k:” in a separate line. Then output Q lines, each line is the minimum value of   . Output a blank line after every test case.
 

Sample Input

     
     
2 5 3 6 2 2 4 2 1 4 0 2 2 7 7 2 0 1 1 1
 

Sample Output

     
     
Case #1: 6 4 Case #2: 0 0


#include <iostream>
#include <algorithm>
#include <string.h>
#define MAXN 100001

using namespace std;

class Node
{
	public:
	
	int l, r;
};

class SGtree
{
	public:
	
	Node node[MAXN << 2];
	
	int num_left[20][MAXN];
	int sg_node[20][MAXN];
	long long sum[18][MAXN];
	int parray[MAXN];
	
	void init();
	void Maketree(int i, int d, int l, int r);
	int Query(int i, int d, int x, int y, int k);
}tree;

void SGtree::init()
{
	memset(num_left, 0, sizeof(0));
	memset(sg_node, 0, sizeof(sg_node));
	memset(node, 0, sizeof(node));
	memset(sum, 0, sizeof(sum));
}

void SGtree::Maketree(int o, int d, int l, int r)
{
	node[o].l = l;
	node[o].r = r;
	if (l == r)
	{
		return ;
	}
	
	int mid = (l + r) >> 1;
	int issame = mid - l + 1;
	
	for (int i = l; i <= r; i++)
	{
		if (sg_node[d][i] < parray[mid])
		{
			issame--;
		}
	}
	
	int pl = l, pr = mid + 1;
	
	for (int i = l; i <= r; i++)
	{
		if (i == l)
		{
			num_left[d][i] = 0;
			sum[d][i] = 0;
		}
		else
		{
			num_left[d][i] = num_left[d][i - 1];
			sum[d][i] = sum[d][i - 1];
		}
		
		if (sg_node[d][i] < parray[mid])
		{
			num_left[d][i]++;
			sum[d][i] += sg_node[d][i];
			sg_node[d + 1][pl++] = sg_node[d][i];
		}
		else if (sg_node[d][i] > parray[mid])
		{
			sg_node[d + 1][pr++] = sg_node[d][i];
		}
		else
		{
			if (issame > 0)
			{
				issame--;
				num_left[d][i]++;
				sum[d][i] += sg_node[d][i];
				sg_node[d + 1][pl++] = sg_node[d][i];
			}	
			else
			{
				sg_node[d + 1][pr++] = sg_node[d][i];	
			}
		}
	}
	
	Maketree(2 * o, d + 1, l, mid);
	Maketree(2 * o + 1, d + 1, mid + 1, r);
}

long long sumleft, numleft;

int SGtree::Query(int i, int d, int x, int y, int k)
{
	int l = node[i].l;
	int r = node[i].r;
	int mid = (l + r) >> 1;
	long long temp = 0;
	
	if (l == r)
	{
		return sg_node[d][x];
	}
	
	int lnum, ltornum;
	
	if (x == node[i].l)
	{
		lnum = 0;
		temp = sum[d][y];
	}
	else
	{
		lnum = num_left[d][x - 1];
		temp = sum[d][y] - sum[d][x - 1];
	}
	
	ltornum = num_left[d][y] - lnum;
	
	if (ltornum >= k)
	{
		return Query(i * 2, d + 1, l + lnum, l + lnum + ltornum - 1, k);
	}
	else
	{
		int a = x - l - lnum;
		int b = y - x - ltornum;
		numleft += ltornum;
		sumleft += temp;
		return Query(i * 2 + 1, d + 1, mid + a + 1, mid + a + b + 1, k - ltornum);
	}
}

void solve(int x, int y)
{
	int mid = (y - x + 2) >> 1;
	numleft = 0;
	sumleft = 0;
	
	int m = tree.Query(1, 1, x, y, mid);
	
	long long ans = m * numleft - sumleft;
	
	long long temp = tree.sum[0][y] - tree.sum[0][x - 1] - sumleft;
	
	ans += temp - m * (y - x + 1 - numleft);
	
	cout << ans << endl;
}

void input()
{
	int t, n, m, x, y, c = 0;
	
	cin >> t;
	
	while (t--)
	{
		tree.init();
		scanf("%d", &n);
		
		for (int i = 1; i <= n; i++)
		{
			scanf("%d", &tree.parray[i]);
			tree.sg_node[1][i] = tree.parray[i];
			tree.sum[0][i] = tree.sum[0][i - 1] + tree.parray[i];
		}
		
		sort(tree.parray + 1, tree.parray + n + 1);
		tree.Maketree(1, 1, 1, n);
		scanf("%d", &m);		
		cout << "Case #" << ++c << ':' << endl;

		while (m--)
		{
			scanf("%d %d", &x, &y);
			x++, y++;
			solve(x, y);
		}
		cout << endl;
	}
}

int main()
{
	input();
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值