Repetitive K-Sums
Help Bob find the sequence set by Alice. Problem statement here.
Problem Setter
Anton Lunyov
Problem Tester
Khongor Enkhbold
Difficulty Level
Medium-Hard
Required Knowledge
Dynamic Programming, Combinatorics, Sum of K-subset.
Approach
Firstly, For a given N and K the number of terms in K-Sums sequence will be n+k-1 Choose k.
Let these terms be M and represented by array Sums[] and original array be A[]
After sorting this Sums[] sequence we can say that A[0] = Sums[0]/K. And erase S[0] as it won’t have information for any other terms in sequence A[].
Hence for any Sums[i] we note that after erasing all k-sums involving numbers a[0], a[1], …, a[i-1] the minimal k-sum is a[i] + (k-1) * a[0], hence giving the next a[i] value.
We do the removing part using dynamic programming. Erasing all the terms in S[] that are made using previous K sum elements of A[] till A[i].
Also we don’t need to erase k-sums that contain a[n-1] since we have already restored the whole array by then.
Setter’s Code :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
// returns n! / k! / (n-k)! = n * (n-1) * ... * (n-k+1) / 1 / 2 / ... / k
// = n / 1 * (n-1) / 2 * (n-2) / 3 * ... * (n-k+1) / k
int bin(int n, int k) {
if(k > n - k) {
k = n - k;
}
int p = 1;
for (int i = 1; i <= k; ++i) {
p *= n + 1 - i;
p /= i;
}
return p;
}
int n, k;
LL a[100000];
multiset<LL> kSums;
// recursive routine that erase all sums having a[i] as the last element
// @j is the current a[j] we want to add to the sum
// @cnt is the count of numbers in the current sum
// @sum is the value of the sum
void rec(int i, int j, int cnt, LL sum) {
if (cnt == k) {
kSums.erase(kSums.find(sum));
} else {
rec(i, j, cnt + 1, sum + a[j]);
if (j < i) {
rec(i, j + 1, cnt, sum);
}
}
}
int main() {
int T;
scanf("%d", &T);
assert ( 1<=T<=100000);
for (int t = 0; t < T; ++t) {
// n and k defined globally
scanf("%d%d", &n, &k);
assert ( 1<=n<=100000);
assert ( 1<=k<=100000);
int m = bin(n + k - 1, k); // the total number of k-sums
assert ( 1<=m<=100000);
// input k-sums and insert them into multiset
kSums.clear();
for (int i = 0; i < m; ++i) {
LL kSum;
scanf("%lld", &kSum);
assert ( 1<=kSum<=1000000000000000000L);
kSums.insert(kSum);
}
// the minimal k-sum is alsways a[0] * k
a[0] = *(kSums.begin()) / k;
kSums.erase(kSums.begin());
for (int i = 1; i < n; ++i) {
// after erasing all k-sums involcing numbers a[0], a[1], ..., a[i-1]
// the minimal k-sum is a[i] + (k-1) * a[0]
a[i] = *(kSums.begin()) - (k - 1) * a[0];
// we don't need to erase ksums that contain a[n-1]
// since we have already restored the whole array
// and this important in the case n=2 since k could be 99999 in this case
// which could lead to stack overflow and TL
if (i < n - 1) {
rec(i, 0, 1, a[i]);
}
}
for (int i = 0; i < n; ++i) {
printf("%lld%c", a[i], i < n - 1 ? ' ' : '\n');
}
}
return 0;
}
Tester’s Code
#include <cmath>
#include <set>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define MAX 100000
int n, k;
long long a[MAX];
multiset<long long> s;
void rec(int m, int idx, int at, long long sum) {
if (at == k - 1) {
s.erase(s.find(sum));
}
else {
rec(m, idx, at + 1, sum + a[idx]);
if (idx + 1 < m) {
rec(m, idx + 1, at, sum);
}
}
}
int main() {
// freopen("input.txt", "r", stdin);
int T;
scanf("%d", &T);
while (T--) {
s.clear();
scanf("%d%d", &n, &k);
char c;
scanf("%c", &c);
while (true) {
long long temp;
scanf("%lld%c", &temp, &c);
s.insert(temp);
if (c == '\n') break;
}
a[0] = *s.begin() / k;
s.erase(s.begin());
for (int i = 1; i < n; i++) {
a[i] = ((*s.begin()) - a[0] * (k - 1));
rec(i + 1, 0, 0, a[i]);
}
for (int i = 0; i < n - 1; i++) printf("%lld ", a[i]); printf("%lld\n", a[n - 1]);
}
return 0;
}