【Educational CF Round 86 (Rated for Div. 2) / 1342D】- D. Multiple Testcases - 优先队列?| 二分 | 后缀和(多解+详解)

D. Multiple Testcases


time limit per test :2 seconds                                memory limit per test :256 megabytes
input :standard input                                           output :standard output

So you decided to hold a contest on Codeforces. You prepared the problems: statements, solutions, checkers, validators, tests... Suddenly, your coordinator asks you to change all your tests to multiple testcases in the easiest problem!

Initially, each test in that problem is just an array. The maximum size of an array is k. For simplicity, the contents of arrays don't matter. You have n tests — the i-th test is an array of size mi (1 ≤ mi ≤ k).

Your coordinator asks you to distribute all of your arrays into multiple testcases. Each testcase can include multiple arrays. However, each testcase should include no more than c1 arrays of size greater than or equal to 1 (≥1), no more than c2 arrays of size greater than or equal to 2, …, no more than ck arrays of size greater than or equal to k. Also, c1 ≥ c2 ≥ ⋯ ≥ ck.

So now your goal is to create the new testcases in such a way that:

each of the initial arrays appears in exactly one testcase;
for each testcase the given conditions hold;
the number of testcases is minimum possible.
Print the minimum possible number of testcases you can achieve and the sizes of arrays included in each testcase.

Input


The first line contains two integers n and k (1 ≤ n,k ≤ 2\cdot10^5) — the number of initial tests and the limit for the size of each array.

The second line contains n integers m1,m2,…,mn (1 ≤ mi ≤ k) — the sizes of the arrays in the original tests.

The third line contains k integers c1,c2,…,ck (n ≥ c1 ≥ c2 ≥ ⋯ ≥ ck ≥ 1); ci is the maximum number of arrays of size greater than or equal to i you can have in a single testcase.

Output


In the first line print a single integer ans (1 ≤ ans ≤ n) — the minimum number of testcases you can achieve.

Each of the next ans lines should contain the description of a testcase in the following format:

t a1 a2 … at (1 ≤ t ≤ n) — the testcase includes t arrays, ai is the size of the i-th array in that testcase.

Each of the initial arrays should appear in exactly one testcase. In particular, it implies that the sum of t over all ans testcases should be equal to n.

Note that the answer always exists due to ck ≥ 1 (and therefore c1 ≥ 1).

If there are multiple answers, you can output any one of them.

Examples

input

4 3
1 2 2 3
4 1 1

output

3
1 2
2 1 3
1 2

input

6 10
5 8 1 10 8 7
6 6 4 4 3 2 2 2 1 1

output

2
3 8 5 7
3 10 8 1

input

5 1
1 1 1 1 1
5

output

1
5 1 1 1 1 1

input

5 1
1 1 1 1 1
1

output

5
1 1
1 1
1 1
1 1
1 1

Note


In the first example there is no way to distribute the tests into less than 3 testcases. The given answer satisfies the conditions: each of the testcases includes no more than 4 arrays of size greater than or equal to 1 and no more than 1 array of sizes greater than or equal to 2 and 3.

Note that there are multiple valid answers for this test. For example, testcases with sizes [[2],[1,2],[3]] would also be correct.

However, testcases with sizes [[1,2],[2,3]] would be incorrect because there are 2 arrays of size greater than or equal to 2 in the second testcase.

Note the difference between the third and the fourth examples. You can include up to 5 arrays of size greater than or equal to 1 in the third example, so you can put all arrays into a single testcase. And you can have only up to 1 array in the fourth example. Thus, every array should be included in a separate testcase.

题目大意

给n个单样例,每个单样例有长度不超过k的数组,问n个单样例最少能划分成几个满足限制的多样例,要求每个多样例中长度大于等于i的数组个数不超过c[i]个,并输出每个多样例中数组的个数以及各个数组的长度(这很好理解,联系实际,如果不同多样例差距太大,在某些极端样例时会tle)

或者用物品去理解也可以,每个背包最多只能装体积超过i的物品c[i]个,问最少要多少背包。(为了方便,下面我们用物品来说明)

思路

首先,物体体积越大,限制越大,那么我们一定会想到先放体积大的物体,而且这一定是对的,因为比如体积为i的物品有3个,c[i] = 5,放完体积为i的物品后剩余两个位置我们可以放体积比i小的物品,并且体积比i小的物品一定可以放至少两个,因为c[j] >= c[i] (j < i)。

1.一个思路是我们用优先队列去维护背包,队头是已装物品最少的背包,已装物品少并且还能放下当前物品的背包优先使用,如果已使用的背包装完体积为i的物品后,体积为i的物品还有剩余,那么再使用一个背包。

(这种方法用时171ms,本以为这样一次放多个物品,在极端样例下复杂度nlogn,均摊复杂度应该很优秀,但没想到比不过复杂度始终为nlogn的方法)

D - Multiple Testcases GNU C++11 Accepted 171 ms 10700 KB

2.上一个思路极端情况需要每次把已使用并且还能使用的背包拿去放物品,也就是把已开始使用的背包循环了一遍,所以我们可以直接在二重循环上优化,优化方法就是如果c[m[i]] > c[m[i + 1]]才重新从第一个背包开始装,不然从上次的背包开始继续装(很好理解,如果相等,那么前面的背包已经装满了,没必要从第一个背包开始去试)

(竟然也是用时171ms,如果不优化,亲测Time limit exceeded on test 16,这个方法是从别人代码里看到的)

D - Multiple Testcases GNU C++11 Accepted 171 ms 7100 KB

后两个思路需要先理解一个事实,当我们知道背包的数量num,那么将物品排序后,将每个物品依次放入num个背包中是最优的,这其实算是贪心,看上去显然思考后又不那么显然。

比如物品为1,2,3,4,5,6,背包数量为2,那么依次放入后第一个背包是1,3,5,第二个背包是2,4,6,假设这种方法不是最优的,也就是存在更优的,比如我们希望第一个背包是1,2,5,第二个背包是3,4,6,那么其实我们可以发现如果3,4放在同一背包中满足限制,那么3,5一定满足限制,也就是第二个方案可行,那么第一个方案一定可行,反之,就不一定了。(一个不严谨的反证法,意思理解就行)

3.那么第三个思路就是去二分背包数量,然后输出即可。

(时间复杂度为稳定的nlogn,用时140ms)

D - Multiple Testcases GNU C++11 Accepted 140 ms

4.但是其实我们可以发现背包数量不用去二分,如果我们计算后缀和,那么背包数量ans = max(ans, (num[i] + c[i] - 1) / c[i]);(其中num[i] = sum(i, n) )这样算出来的背包数量在任何时候都满足使用,并且最小(不是很好解释,但是很好理解)

(本以为就排序nlogn,并且计算背包数量复杂度为O(n),怎么着常数也比第三个思路小,没想到也是140ms)

D - Multiple Testcases GNU C++11 Accepted 140 ms 2400 KB

曾尝试用过map reverse遍历,按理说复杂度应该是O(nlogn),因为体积为i的物品用完后,会erase掉,不会遍历不存在物品的体积值,所以遍历O(n),然后map复杂度logn,没想到Time limit exceeded on test 16,可能常数太大了吧。

D - Multiple Testcases GNU C++11 Time limit exceeded on test 16 2000 ms 10800 KB

(具体实现看下面代码)

代码

1.优先队列

#include 
using namespace std;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long ll;
typedef unsigned long long ull;
typedef pair PII;
typedef pair pll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;
ll qpow(ll base, ll n){ll ans = 1; while (n){if (n & 1) ans = ans * base % mod; base = base * base % mod; n >>= 1;} return ans;}
ll gcd(ll a, ll b){return b ? gcd(b, a % b) : a;}
int m[N], c[N], num[N];
vector ans[N];
priority_queue, greater> q;//小根堆
int sum[N];
int main()
{
	int n, k;
	cin >> n >> k;
	for (int i = 1; i <= n; ++ i){
		scanf("%d", &m[i]);
		num[m[i]] ++;
	}
	for (int i = 1; i <= k; ++ i){
		scanf("%d", &c[i]);
	}
	int tot = 0;
	q.push(mp(0, ++ tot));
	sum[tot] = 0;
	for (int i = k; i >= 1; -- i){
		if (!num[i]) continue;
		PII tmp = q.top();
		int cnt = tmp.fi, pos = tmp.se;
		if (cnt < c[i]){
			do{
				q.pop();
				int x =min(c[i] - cnt, num[i]);
				num[i] -= x;
				ans[pos].pb(mp(i, x));
				q.push(mp(cnt + x, pos));
				sum[pos] = cnt + x;
				tmp = q.top();
				cnt = tmp.fi, pos = tmp.se;
			}while (cnt < c[i] && num[i]);//尝试放已使用并且还能放的背包
		}
		while (num[i]){
			int x = min(c[i], num[i]);
			ans[++ tot].pb(mp(i, x));
			q.push(mp(x, tot));
			sum[tot] = x;
			num[i] -= x;
		}

	}
	printf("%d\n", tot);
	for (int i = 1; i <= tot; ++ i){
		printf("%d ", sum[i]);
		for (int j = 0; j < ans[i].size(); ++ j){
			int val = ans[i][j].fi, cnt = ans[i][j].se;
			for (int k = 1; k <= cnt; ++ k){
				printf("%d ", val);
			}
		}
		puts("");
	}
	return 0;
}

2.遍历已使用背包+优化 

#include 
using namespace std;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long ll;
typedef unsigned long long ull;
typedef pair PII;
typedef pair pll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;
ll qpow(ll base, ll n){ll ans = 1; while (n){if (n & 1) ans = ans * base % mod; base = base * base % mod; n >>= 1;} return ans;}
ll gcd(ll a, ll b){return b ? gcd(b, a % b) : a;}
int m[N], c[N];
vector  ans[N];

int main()
{
	int n, k;
	cin >>n >> k;
	for (int i = 1; i <= n; ++ i) scanf("%d", &m[i]);
	for (int i = 1; i <= k; ++ i) scanf("%d", &c[i]);
	sort (m + 1, m + 1 + n);
	int p = 1, num = 1;
	for (int i = n; i ; -- i){
		if (c[m[i]] > c[m[i + 1]]) p = 1;//没错,就这一句优化就让T变为171ms,和优先队列一样快
		while (c[m[i]] == (int)ans[p].size()) num = max(num, ++ p);
		ans[p].pb(m[i]);
	}
	printf("%d\n", num);
	for (int i = 1; i <= num; ++ i){
		printf("%d ", (int)ans[i].size());
		for (auto j : ans[i]) printf("%d ", j);
		puts("");
	}
	return 0;
}

3.二分答案

#include 
using namespace std;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long ll;
typedef unsigned long long ull;
typedef pair PII;
typedef pair pll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;
ll qpow(ll base, ll n){ll ans = 1; while (n){if (n & 1) ans = ans * base % mod; base = base * base % mod; n >>= 1;} return ans;}
ll gcd(ll a, ll b){return b ? gcd(b, a % b) : a;}
int m[N], c[N], a[N];
int n, k;
bool judge(int num){
	for (int i = 1; i <= num; ++ i){
		int tot = 0;
		for (int j = i; j <= n; j += num) a[++ tot] = m[j];
		for (int j = 1; j <= tot; ++ j){
			if (tot - j + 1 > c[a[j]]) return false;
		}
	}
	return true;
}
int main()
{
	cin >>n >> k;
	for (int i = 1; i <= n; ++ i) scanf("%d", &m[i]);
	for (int i = 1; i <= k; ++ i) scanf("%d", &c[i]);
	sort(m + 1, m + 1 + n);
	int l = 1, r = n;
	while (l < r){
		int mid = (l + r) >> 1;
		if (judge(mid)) r = mid;
		else l = mid + 1;
	}
	printf("%d\n", r);
	for (int i = 1; i <= r; ++ i){
		printf("%d ", 1 + (n - i) / r);
		for (int j = i; j <= n; j += r) printf("%d ", m[j]);
		puts("");
	}
	return 0;
}

4.后缀和求答案

#include 
using namespace std;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long ll;
typedef unsigned long long ull;
typedef pair PII;
typedef pair pll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;
ll qpow(ll base, ll n){ll ans = 1; while (n){if (n & 1) ans = ans * base % mod; base = base * base % mod; n >>= 1;} return ans;}
ll gcd(ll a, ll b){return b ? gcd(b, a % b) : a;}
int m[N], c[N], num[N];

int main()
{
	int n, k;
	cin >> n >> k;
	for (int i = 1; i <= n; ++ i) {
		scanf("%d", &m[i]);
		num[m[i]] ++;
	}
	sort(m + 1, m + 1 + n);
	for (int i = 1; i <= k; ++ i) {
		scanf("%d", &c[i]);
	}
	int ans = (num[k] + c[k] - 1) / c[k];
	for (int i = k - 1; i; -- i) {
		num[i] += num[i + 1];
		ans = max(ans, (num[i] + c[i] - 1) / c[i]);
	}
	printf("%d\n", ans);
	for (int i = 1; i <= ans; ++ i) {
		printf("%d ", 1 + (n - i) / ans);
		for (int j = i; j <= n; j += ans) printf("%d ", m[j]);
		puts("");
	}
	return 0;
}

5.map reverse遍历 + erase ---Time limit exceeded on test 16

#include 
using namespace std;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long ll;
typedef unsigned long long ull;
typedef pair PII;
typedef pair pll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;
ll qpow(ll base, ll n){ll ans = 1; while (n){if (n & 1) ans = ans * base % mod; base = base * base % mod; n >>= 1;} return ans;}
ll gcd(ll a, ll b){return b ? gcd(b, a % b) : a;}
int m[N], c[N];
map num;
map ::reverse_iterator it, itt;
vector ans[N];
int cnt[N];
int main()
{
	int n, k;
	cin >> n >> k;
	for (int i = 1; i <= n; ++ i){
		scanf("%d", &m[i]);
		num[m[i]] ++;
	}
	for (int i = 1; i <= k; ++ i){
		scanf("%d", &c[i]);
	}
	int tot = 0;
	int sum = 0;
	while (sum < n){
		tot ++;
		int tmp = 0;
		int x, y;
		for (it = num.rbegin(); it != num.rend(); ){
			int i = it->first;
			int s = it->second;
			if (!s){
				it ++;
				continue;
			}
			if (c[i] - tmp <= 0) {
				it ++;
				continue;
			}
			x = i;
			y = min(s, c[i] - tmp);
			ans[tot].pb(mp(x, y));
			cnt[tot] += y;
			tmp += y;
			s -= y;
			if (s) num[i] = s, it ++;
			else {
				num.erase(--(it).base());
			}
		}
		sum += cnt[tot];
	}
	printf("%d\n", tot);
	for (int i = 1; i <= tot; ++ i){
		printf("%d ", cnt[i]);
		for (int j = 0; j < (int)ans[i].size(); ++ j){
			int x = ans[i][j].fi;
			int y = ans[i][j].se;
			for (int k = 1; k <= y; ++ k) printf("%d ", x);
		}
		puts("");
	}
	return 0;
}

 

你可能感兴趣的:(【Educational CF Round 86 (Rated for Div. 2) / 1342D】- D. Multiple Testcases - 优先队列?| 二分 | 后缀和(多解+详解))