Codeforces 700D Huffman Coding on Segment 莫队算法

D. Huffman Coding on Segment
time limit per test
4 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Alice wants to send an important message to Bob. Message a = (a1, ..., an) is a sequence of positive integers (characters).

To compress the message Alice wants to use binary Huffman coding. We recall that binary Huffman code, or binary prefix code is a function f, that maps each letter that appears in the string to some binary string (that is, string consisting of characters '0' and '1' only) such that for each pair of different characters ai and aj string f(ai) is not a prefix of f(aj) (and vice versa). The result of the encoding of the message a1, a2, ..., an is the concatenation of the encoding of each character, that is the string f(a1)f(a2)... f(an). Huffman codes are very useful, as the compressed message can be easily and uniquely decompressed, if the function f is given. Code is usually chosen in order to minimize the total length of the compressed message, i.e. the length of the string f(a1)f(a2)... f(an).

Because of security issues Alice doesn't want to send the whole message. Instead, she picks some substrings of the message and wants to send them separately. For each of the given substrings ali... arishe wants to know the minimum possible length of the Huffman coding. Help her solve this problem.

Input

The first line of the input contains the single integer n (1 ≤ n ≤ 100 000) — the length of the initial message. The second line contains n integers a1, a2, ..., an (1 ≤ ai ≤ 100 000) — characters of the message.

Next line contains the single integer q (1 ≤ q ≤ 100 000) — the number of queries.

Then follow q lines with queries descriptions. The i-th of these lines contains two integers li and ri(1 ≤ li ≤ ri ≤ n) — the position of the left and right ends of the i-th substring respectively. Positions are numbered from 1. Substrings may overlap in any way. The same substring may appear in the input more than once.

Output

Print q lines. Each line should contain a single integer — the minimum possible length of the Huffman encoding of the substring ali... ari.

Example
input
7
1 2 1 3 1 2 1
5
1 7
1 3
3 5
2 4
4 4
output
10
3
3
5
0
Note

In the first query, one of the optimal ways to encode the substring is to map 1 to "0", 2 to "10" and 3 to "11".

Note that it is correct to map the letter to the empty substring (as in the fifth query from the sample).



给你一串数,多次询问问你一段区间[L,R]之间数字的哈夫曼编码最短是多少。


构造哈夫曼树时,每次选择权值最小的两个合并。这题同样如此。莫队算法统计区间内每个数字的出现次数,再统计出现次数为一定量的有几个数。对于出现次数1~sqrt(n)的,暴力两两合并,把合并之后的结果再更新。剩下的一定是出现次数大于sqrt(n)的,这些数字一定不多于sqrt(n)个,我们利用优先队列合并这些数。


复杂度O(nsqrt(n)logn)


#include <cstdio>
#include <iostream>
#include <string.h>
#include <string> 
#include <map>
#include <queue>
#include <deque>
#include <vector>
#include <set>
#include <algorithm>
#include <math.h>
#include <cmath>
#include <stack>
#include <iomanip>
#define mem0(a) memset(a,0,sizeof(a))
#define meminf(a) memset(a,0x3f,sizeof(a))
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
const int maxn = 100005, inf = 0x3f3f3f3f;
const ll llinf = 0x3f3f3f3f3f3f3f3f;
const ld pi = acos(-1.0L);
int a[maxn], cnt[maxn], fre[maxn], fr[maxn];
ll ans[maxn];
vector<int> v;

struct query {
	int l, r, id, k;
};
query q[maxn];

bool cmp(query a, query b) {
	return a.k < b.k || (a.k == b.k&&a.r < b.r);
}

void update(int pos, int val) {
	fre[cnt[pos]]--;
	cnt[pos] += val;
	fre[cnt[pos]]++;
}

int main() {
	int n, i, j, k, m, l, r;
	scanf("%d", &n);
	mem0(cnt);mem0(ans);
	int size = sqrt(n) + 1;
	for (i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
		cnt[a[i]]++;
		if (cnt[a[i]] == size) v.push_back(a[i]);
	}
	scanf("%d", &m);
	for (i = 1; i <= m; i++) {
		scanf("%d%d", &q[i].l, &q[i].r);
		q[i].id = i; q[i].k = q[i].l / size;
	}
	sort(q + 1, q + m + 1, cmp);
	l = 1; r = 0;
	mem0(cnt); mem0(fre);
	fre[0] = n;
	for (i = 1; i <= m; i++) {
		while (l < q[i].l) update(a[l], -1), l++;
		while (l > q[i].l) l--, update(a[l], 1);
		while (r > q[i].r) update(a[r], -1), r--;
		while (r < q[i].r) r++, update(a[r], 1);
		priority_queue<int, vector<int>, greater<int> > pq;
		memcpy(fr,fre,sizeof(int)*(size+5));
		ans[q[i].id] = 0;
		for (j = 0; j < v.size(); j++) if (cnt[v[j]] >= size) pq.push(cnt[v[j]]);
		int last = 0;
		for (j = 1; j < size; j++) {
			if (fr[j] > 0) {
				if (last) {
					fr[j]--; ans[q[i].id] += (ll)(last + j);
					if (last + j < size) fr[last + j]++; else pq.push(last + j);
					last = 0;
				}
				if (fr[j] % 2 == 1) fr[j]--, last = j;
				ans[q[i].id] += fr[j]*j;
				if (2 * j >= size)
					for (k = 1; k <= fr[j] / 2; k++) pq.push(j * 2);
				else fr[j * 2] += fr[j] / 2;
			}
		}
		if (last) pq.push(last);
		while (pq.size() > 1) {
			int b = pq.top(); pq.pop();
			int c = pq.top(); pq.pop();
			ans[q[i].id] += (ll)(b + c);
			pq.push(b + c);
		}
	}
	for (i = 1; i <= m; i++) printf("%I64d\n", ans[i]);
	return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值