An ACM-level problem because it involves "advanced maths". It should not be marked as "Moderate". Other than that, it is a medium level DP one.
*Math modeling in your mind: can you see through the problem statement and figure out that it isa DP-style letter choice process? I couldn't.. This is so crucial, however.
*DP Part: there is dependency between letters: you should pick higher chars then lower chars, and DP choice occurs during this process.
*Maths Part: C(m + n, n) is used, but the numbers are huge, so regular combination formula is not working here. Lucas Theorem is necessary here (http://en.wikipedia.org/wiki/Lucas%27_theorem), which I will try to understand later..
I checked Editorials, understand it and then rewrite their Jave code into my solution in C++ (it looks almost the same).
#include <cmath> #include <cstdio> #include <vector> #include <map> #include <set> #include <unordered_set> #include <string> #include <climits> #include <iostream> #include <algorithm> #include <unordered_map> #include <unordered_set> using namespace std; #define MAX_LEN 2501 #define MOD 1000003 /* Lucas Theorem: to compute large number of C(m, n) */ class compute { long long Fact[MOD], rFact[MOD]; public: void init(){ Fact[0] = rFact[0] = 1; for (long i = 1; i < MOD; i++) { Fact[((int)i)] = (i * Fact[((int)(i - 1))]) % MOD; rFact[((int)i)] = powmod(Fact[((int)i)], MOD - 2); } } long long ways(long k, long n, long r){ return lucas(n - k*(r - 1), r); } long long nCr(long n, long k) { if (n<0 || k<0 || k>n) return 0; return (((Fact[(int)n] * rFact[(int)k]) % MOD) *rFact[(int)(n - k)]) % MOD; } long long lucas(long n, long r) { if (n < MOD) return nCr(n, r); return (nCr(n / MOD, r / MOD) * nCr(n%MOD, r%MOD)) % MOD; } long long powmod(long b, long p) { long long ret = 1, a = b%MOD; while (p>0) { if (p % 2 == 1) ret = (ret*a) % MOD; a = (a*a) % MOD; p >>= 1; } return ret; } }; compute cp; /////////////////////// vector<int> howMany(26); long long call(int pos, int taken, int n, int k, vector<vector<long>> &dp) { if (pos > 25) return 1; if (dp[pos][taken] != -1) return dp[pos][taken]; long long ret = call(pos + 1, taken, n, k, dp); for (int i = 1; i <= howMany[pos]; i++) { if ((i + taken) > n) break; long long curr = cp.ways(k, n - taken, i) * call(pos + 1, taken + i, n, k, dp); curr %= MOD; ret += curr; if (ret >= MOD) ret -= MOD; } dp[pos][taken] = ret; return ret; } int main() { int n, k; cin >> n >> k; char buf[MAX_LEN] = {0}; scanf("%s", buf); string s(buf); size_t len = s.length(); vector<vector<long>> dp(26, vector<long>(len + 2, -1)); for(char c : s) howMany[c - 'A'] ++; cp.init(); long long ret = call(0, 0, n, k, dp); cout << ret << endl; return 0; }