cafelier のSRM参加記録です。コンテスト中に考えてたことを執拗に全部書き残すとどうなるだろうかという試み本番中にこういうコードが書きたかったなあ、という後で書いた反省コードを書き残す試み
スパムが来たのでしばらくコメント欄をはてなユーザ限定にしています、すみません、
あとで | |
static const int MODVAL = 500500573; // prime struct mint { int val; mint():val(0){} mint(int x):val(x%MODVAL) {} mint(LL x):val(x%MODVAL) {} }; mint operator+(mint x, mint y) { return x.val+y.val; } mint operator-(mint x, mint y) { return x.val-y.val+MODVAL; } mint operator*(mint x, mint y) { return LL(x.val)*y.val; } mint POW(mint x, int e) { mint v = 1; for(;e;x=x*x,e>>=1) if(e&1) v=v*x; return v; } mint operator/(mint x, mint y) { return x * POW(y, MODVAL-2); } class ProductAndSum { public: int getSum(int p2, int p3, int p5, int p7, int S) { // Precompute n!, 1/n!, and 111...111 vector<mint> FACT(2501), FACT_INV(2501), ONES(2501); FACT[0] = FACT_INV[0] = 1; ONES[0] = 0; for(int n=1; n<FACT.size(); ++n) { FACT[n] = FACT[n-1] * n; FACT_INV[n] = 1 / FACT[n]; ONES[n] = ONES[n-1]*10 + 1; } mint answer = 0; // Exhaustive search over the numbers of 4,6,8,9s : approximately 33*50*50*100/4 ~ 2M iterations for(int v8=0; v8*3<=p2; ++v8) for(int v4=0; v4*2+v8*3<=p2; ++v4) for(int v9=0; v9*2<=p3; ++v9) for(int v6=0; v6+v4*2+v8*3<=p2 && v6+v9*2<=p3; ++v6) { // then, the numbers of 1,2,3s will follow int v[] = {-1, -1, p2-v8*3-v4*2-v6, p3-v9*2-v6, v4, p5, v6, p7, v8, v9}; { int v1 = S; for(int i=2; i<=9; ++i) v1 -= i * v[i]; if( v1 < 0 ) continue; v[1] = v1; } int N = accumulate(v+1, v+10, 0); // Now, let's compute the sum of all integers, who have N digits and #d = v[d], in constant time! // It can be calculated as follows: // Q: how many of the integers have, say "1" in the last digit? // A: it is, of course, C(N-1, v[1]-1, v[2], ..., v[9]) // where C(k,a,b,..) is the # of ways of spliting k elements into groups of size a, b, ... // So, the sum of the least digits of the integers are // X = \Sigma_{d=1..9} (d * C(N-1, v[1], v[2], ..., v[d]-1, ..., v[9])) // By the same argument, the sum of the second-last digits (10-no-kurai in japanese) is // 10 * X // For hundreds, the sum is 100*X, and so on. Eventualy, the sum of whole integers is // t = (1+10+...+10^N) * X // = 111...111 * \Sigma_{d=1..9} (d * C(N-1, v[1], v[2], ..., v[d]-1, ..., v[9])) // Let us simplify the formula by using C(k,a,b,...) = k! / a!b!... // t = 111...111 * \Sigma_{d=1..9} (d * (N-1)! / v[1]! / ... / v[9]! * v[d]) // = 111...111 * (N-1)! / v[1]! / ... / v[9]! * \Sigma_{d=1..9} (d*v[d]) // = 111...111 * (N-1)! / v[1]! / ... / v[9]! * S // That's it! mint t = ONES[N] * FACT[N-1] * S; for(int i=1; i<=9; ++i) t = t * FACT_INV[v[i]]; answer = answer + t; } return answer.val; } };
presented by cafelier/k.inaba under
cafelier2011/03/21 12:211000、Σを消そうとして頑張って式変形したけれど、よく考えたらこのΣは1~9までしか回らないので、普通に最初に立式した通りに計算すればよかったのではないだろうか。反省
delta23232011/03/22 01:36初めまして,いつも勉強させて頂いています。
自分のコードでなのですが,式変形した場合,テストケースで最大約600msかかり,しないとTLEを起こしてしまいました。式変形をする事も出題した方の意図だったのでしょうか。
cafelier2011/03/22 10:58自分のコードでも試してみたところ、確かに
mint X = 0;
for(int d=1; d<=9; ++d){
mint f = d*v[d]*FACT[N-1];
for(int i=1; i<=9; ++i)
f = f * FACT_INV[v[i]];
X = X + f;
}
answer = answer + X * ONES[N];
でTLEでした。なるほど。Σ と C(...) の計算で x9x9 で 2Mx81 回の整数演算となると厳しかったですね。なかなか絶妙なラインの問題設定でした。