One Point Solution

  • Subscribe to our RSS feed.
  • Twitter
  • StumbleUpon
  • Reddit
  • Facebook
  • Digg

Friday, 24 February 2012

SRM 534: Div1 500, EllysNumbers

Posted on 16:44 by Unknown
I have finished coding an idea I got during the morning while wasting time on a typical sub-human education system queue. This problem is fun and cute and almost makes up for the terrible Fake difficulty from the 250.

EllysNumbers
You have a list of 500 special numbers, each from 1 to 109. And also a number n from 2 to 1018, inclusive. Count the total number of subsets of the list of special numbers such that a) All the numbers in the set are pair-wise coprime. And b) The product of all the numbers in the set is equal to n.

For simplicity, let us just get rid of any special number that is not a divisor of n. If it is not a divisor, we cannot use it in the product.

This is a solution that is conceptually simple and easy to prove that runs under the 2 seconds limit. But it requires quite an amount of implementation steps. Funnily enough, after coding the solution I learned that ACRush's top score solution is basically the same, but he can implement it in far less lines. Well, I got to accept that my programs are usually longer than average. It is because I am very strict to myself in following some coding style rules I imposed myself. Now that I notice, the reasons for each of these rules could make a nice discussion for another day.


Anyway, the key to this problem is how to ensure that all the numbers picked in your subset are pairwise coprime. This time it is best not to use the gcd(a,b, ... ) = 1 definition (not directly, at least). Instead, think about factorization.. A group of numbers are pair-wise coprime if no pair of those numbers shares a prime factor.

Why prime factors? Well, for starters numbers in general don't have a lot of prime factors. Note that we are not considering the exponents when making this statement. For example 72, has in our current focus, 2 prime factors: 2 and 3. In reality it is 2*2*2*3*3, but that does not matter yet. Why doesn't it matter? Ok, since the numbers in each subset have to be coprime, you cannot pick two numbers that share a prime factor. You cannot pick 8 and 24 simultaneously. So, for each prime factor of n, you need to pick one of the special numbers. There is an extra condition, this number must have the prime factor raised to the same exponent that n needs. For example, if n = 72 = 2*2*2*3*3, you cannot pick 4 or 36 or 6 as a number in the subset. Because 36 will bring only 2*2 to the matter, but you need 2*2*2 to make 72, but you cannot use any other number that has 2 as factor again.

Now, here is the key result to find an answer what is the maximum number of different prime factors a number less than or equal to 1018 can have? Since we want to maximize the number of factors, we should do something like 2*3*5*7*... (A product of the smallest primes). Find the product of the K smallest primes that is closest to 1018. You will notice that the maximum number of distinct prime factors a number in the input can have is 14.

A typical dynamic programming problem
14... That number is rather small. Now, imagine that we already know the distinct f prime factors of n. Let us index these prime factors and assign each of them a number from 0 to f-1. Now, for each special integer, find the indexes of the prime factors of n it contains. Note that the special integer must either contain the prime factor 0 times or exactly the same number of times as n. In other case, we cannot use the special integer and we must discard it. Since we are assuming that all special integers are divisors of n, there will never be more factors.

Once the special integers have been decomposed into the prime factors of n, there is a recurrence relation that can count the number of subsets: Let f( used[], s) the number of subsets using only the s first special integers if the current list of used prime factors is given by used[]. For each instance of the recurrence, there are two choices: Either use integer # (s-1) or do not use it. In order to use the integer, it must not use any prime factor already in the used[] array. Of course, a base case is when s=0, then we need to have used all factors of n. It becomes something like this:


f(used[], s) {
res = 0
if (s == 0) {
// base case
if (All prime factors of n are in used[]) {
res = 1
} else {
res = 0
}
} else {
if ( (factors in s) does not intersect used[] ) {
// update the used set
// procceed to use smaller sets
res += f( used[] union (factors in s), s - 1)
}
// do not use (no conditions)
res += f(used[], s - 1)
}
return res
}


Now, used[] can be represented as a bitmask . If you do that, let us also represent the factors allowed by each special number as a bitmask too. To check if they intersect , just check if their intersection (used_mask & (mask of special) ) is not 0. Now that the arguments are encoded as a bitmask and a number s. We can verify the recurrence relation is acyclic, and we can use dynamic programming. This yields an algorithm that requires at most (214 * 500) in time units. A small issue is memory, since we always use only the previous value of s when calling the recurrence, we can implement this algorithm in O(214).


Details, However
The dynamic programming algorithm is a rather standard set cover algorithm. We have skipped some work though. We need to factorize n. And n can be too large even for the O(sqrt(n)) algorithm to factorize.

The trick is that the problem requires all prime factors of n to be present in one way or another in the special integers. (Note that the special numbers are at most 109, unlike n) Thus we can just factorize each special integer. The total union of all the factors found can be a factorization of n, or be short of some prime factors. But if you couldn't find enough prime factors in the special integers, we may as well return 0. Because there is no way to ever find n as a product.

#define for_each(q, s) for(typeof(s.begin()) q=s.begin(); q!=s.end(); q++) 
struct EllysNumbers
{
long long getSubsets(long long n, vector <string> sspecial)
{
//===========================================
// a) Parse the input vector<string>, make sure to remove any number that
// is not a divisor of n, because we hate them.
vector<long long> special;
{
string s = accumulate(sspecial.begin(), sspecial.end(), string("") );
istringstream st(s);
long long x;
while (st >> x) {
if (n % x == 0) {
special.push_back(x);
}
}
}
set<int> setPrimeFactors;
//======================================================
// b) Extract prime factors from all special numbers
for_each(q, special) {
int x = *q;
// Factorize x...
for (int y = 2; y <= x/y; y++) {
if (x % y == 0) {
setPrimeFactors.insert(y);
do {
x /= y;
} while (x % y == 0);
}
}
if (x != 1) {
setPrimeFactors.insert(x);
}
}
//=============================================================
// c) Get the amounts of each factor needed to build n.
// also verify that all the factors of n exist in the input.
vector<int> primeFactors( setPrimeFactors.begin(), setPrimeFactors.end() );
int neededFactors[ primeFactors.size() ];
long long z = n;
for (int j=0; j<primeFactors.size(); j++) {
cout << "PF "<<primeFactors[j]<<endl;
int & c = neededFactors[j];
c = 0;
while( z % primeFactors[j] == 0) {
z /= primeFactors[j];
c ++;
}
}
if (z != 1) {
cout << "incomplete "<<z<<endl;
// not all prime factors of n are present...
return 0;
}

// ========================================================
// d )
// Get the factor mask for each of the special numbers.
// However, there is a chance a special number is "incomplete".
// discard those.
//

int factorMask[special.size() ];
for (int i=0; i<special.size(); i++) {
int & mask = factorMask[i];
int x = special[i];
mask = 0;
for (int j=0; j<primeFactors.size(); j++) {
int c = 0 ;
while (x % primeFactors[j] == 0) {
x /= primeFactors[j];
c++;
}
if (c!=0) {
if (c != neededFactors[j]) {
mask = -1;
} else {
mask |= (1 << j);
}
}
}
}
// ==========================================================
// e) The dynamic programming algorithm is rather standard
// now that we have all that info.
// However, we need it in O(2^f) memory.

int f = primeFactors.size();
int s = special.size();
long long dp[2][1<<f];

memset(dp, 0, sizeof(dp) );
// The base case, when all the prime factors are done and no special ints are left
dp[0][ (1<<f) - 1 ] = 1;

for (int t = 1; t <= s; t++) {
for (int mask=0; mask<(1<<f); mask++) {
long long & res = dp[t&1][mask];
res = 0;
// use the (t-1)-th integer
if ( (factorMask[t-1] != -1) && ( (mask & factorMask[t-1]) == 0) ) {
// no intersection, use it.
res += dp[~t&1][ mask|factorMask[t-1] ];
}
// Do not use the (t-1)-th integer
res += dp[~t&1][mask];
}
}

return dp[s&1][0];

}
};



Wow, that was long, I hope someone was able to understand it.
Email ThisBlogThis!Share to XShare to Facebook
Posted in explanation, srm, topcoder | No comments
Newer Post Older Post Home

0 comments:

Post a Comment

Subscribe to: Post Comments (Atom)

Popular Posts

  • TopCoder SRM 557 - finally
    SRM 557 Explanation for division 1 Easy and match recap. Explanations for div2 easy and div2 medium. It feels like it has been ages since t...
  • SRM 589 Editorial
    I have finished writing the editorial for TopCoder SRM 589: http://apps.topcoder.com/wiki/display/tc/SRM+589 . As you most likely noticed. L...
  • SRM 590 recap and editorial
    Another week another Topcoder match. Not a great day. I had a bad flu and still do. Div1 500: The one with Xor Given a list of cards with nu...
  • SRM 546: relief
    I figured I should post something about this SRM. I've been very busy these weeks because the semester is ending and I tried to win a t-...
  • SRM 526: The killing wait for results
    While I wait for results, here is my perspective on this algorithm contest. It began with issues, it had to be postponed 15 minutes. TC has ...
  • SRM 554 div1 hard: TheBrickTowerHardDivOne
    Link to problem statement We got infinitely many bricks of dimensions 1x1x1 and C different colors. Count the number of towers of size 2x2...
  • SRM 533: Div1 500 MagicBoard explanation
    Finally solved it. It is a nice problem that is worth explaining in a post. You have a grid/board of at most 50x50 cells. Some cells contain...
  • Member SRM 505: Part 1
    So, let me explain a couple of problems from a Topcoder Member SRM that I wrote and never got an editorial. BTW, it was the last member SRM....
  • ListedLinks 2012-02-10
    Saturday Morning Breakfast Cereal comics: Grace Hopper's ghost That Oracle engineer blog post Oracle would really not like anyone to se...
  • Codeforces "Good bye 2013" round
    So it was a special round for coders of both divisions, problems ranged from the super easy problem A to the super difficult problems E,F,G....

Categories

  • acm
  • algorithm
  • answers
  • arenaplugin
  • badday
  • behindthescenes
  • bugs
  • c++
  • censorship
  • codechef
  • codeforces
  • contests
  • crocchamp
  • editorial
  • editorial.srm
  • embarrassing
  • explanation
  • gcj2013
  • gmp
  • goodday
  • google
  • googlecodejam
  • greed
  • groklaw
  • health
  • html
  • httpseverywhere
  • implementation
  • ipsc
  • ispc
  • java
  • kawigiedit
  • kindagoodday
  • lamebook
  • languages
  • lego
  • listedlinks
  • marathon
  • nasa
  • offtopic
  • ouch
  • postmortem
  • postportem
  • practical
  • probably_not_a_good_tip
  • problemsetting
  • programming
  • python
  • quora
  • rant
  • recap
  • slightlygoodday
  • snippet
  • srm
  • stl
  • strategy
  • swerc
  • tco
  • tco12
  • tco13
  • tco2012
  • tco2013
  • ternarysearch
  • topcoder
  • tricks
  • ubuntu
  • uva
  • vjass
  • vkcup
  • wc3
  • zinc

Blog Archive

  • ►  2014 (1)
    • ►  January (1)
  • ►  2013 (141)
    • ►  December (14)
    • ►  November (8)
    • ►  October (13)
    • ►  September (11)
    • ►  August (14)
    • ►  July (15)
    • ►  June (13)
    • ►  May (13)
    • ►  April (12)
    • ►  March (11)
    • ►  February (11)
    • ►  January (6)
  • ▼  2012 (94)
    • ►  December (5)
    • ►  October (6)
    • ►  September (8)
    • ►  August (6)
    • ►  July (3)
    • ►  June (5)
    • ►  May (8)
    • ►  April (10)
    • ►  March (20)
    • ▼  February (16)
      • It is the old and worsened ListedLinks 2012-02-27
      • ACM 2012 World finals warmp up I
      • Codeforces 109 1B / 2D - Colliders
      • SRM 534: Div1 500, EllysNumbers
      • SRM 534: Notes that contradict the problem statement
      • Codeforces round #108
      • What is it like to be a (topcoder) problem setter?
      • SRM 533: Div1 500 MagicBoard explanation
      • SRM 533: Failure
      • Codeforces round #107 : "undefined"
      • ListedLinks 2012-02-16
      • Google autocomplete awesomeness
      • ListedLinks 2012-02-10
      • SRM 532: Easy problems can be evil
      • ListedLinks 2012-02-04 - Censorship edition
      • ListedLinks 2012-02-02
    • ►  January (7)
  • ►  2011 (51)
    • ►  December (7)
    • ►  November (12)
    • ►  October (5)
    • ►  September (1)
    • ►  August (3)
    • ►  July (4)
    • ►  June (3)
    • ►  May (7)
    • ►  April (3)
    • ►  March (2)
    • ►  February (1)
    • ►  January (3)
  • ►  2010 (9)
    • ►  December (4)
    • ►  October (1)
    • ►  June (1)
    • ►  May (1)
    • ►  January (2)
  • ►  2009 (1)
    • ►  December (1)
Powered by Blogger.

About Me

Unknown
View my complete profile