import java.io.*; import java.util.*; import java.awt.geom.*; /** * Solution to Component Testing * * @author vanb * * We can envision this problem as a Max Flow problem. Build a Max Flow graph like this: * - A node for every engineer * - A node for every component * - The Source node linked to every Engineer node. Weight = # of components that engineer can test * - Every Component node linked to the Sink node. Weight = # of tests that component needs * - Every Engineer node linked to every Component node. Weight = 1 * * Of course, since there can be 10^4 job titles, and 10^5 engineers per job title, * there can be 10^9 engineers (and likewise 10^9 components), so this is way too big for Ford/Fulkerson. * But Max Flow is the same as Min Cut. Look at it as a Min Cut problem. * * Suppose we start by putting all of the Source->Engineer edges in the cut. * The cost of this cut is the sum of all jobs for all engineers. * What happens if we remove a Source->Engineer edge from the cut & put it back in the graph? * Then, we need to remove all of the edges from that Engineer node to every Component node that * is still in the graph. Each of those has weight 1, so the cost of the cut changes by: * * new value = old value - [Jobs for that engineer] + [# of Components NOT in the cut] * * Now, suppose we take a Component->Sink edge from the graph, and put it in the cut. * Then, we can take out of the cut all of the edges from Engineers not in the cut to * that Component. The change in the value of the cut is: * * new value = old value + [Tasks for that Component] - [# of Engineers NOT in the cut] * * So, here’s our strategy: * - Sort both the Engineers and Components by numbers of tasks * - Remove engineers from the cut from largest to smallest * --- For each removed engineer, add Components from smallest to largest * --- Only add components if there’s an improvement: [Tasks for that Component] < [# of Engineers NOT in the cut] * - If min cut = sum of all component needs, then we’ve got a “Yes”. If min cut < that sum, the answer is “No” * * Note that once [# of Engineers NOT in the cut] is bigger than any [Tasks for that Component], * then all components are in the cut. That’s a complete cut, and there’s no need to go any further. * But, [Tasks for that Component] is capped at 100,000, so there’s never any need to go any further than 100,000 engineers. * * We remove components by comparing [Tasks for that Component] to [# of Engineers NOT in the cut]. * But, [Tasks for that Component] is the same for every component in a class. * So, we don’t have to go through the components one-by-one, we can remove them in bulk. * There are at most 10,000 Component Types. With the engineers, that's a max of 110,000 operations. * That's WAY better than 10^9 * * Note that Engineers are independent of Components – that is, * the effect of manipulating an Engineer is dependent only on the number of Components in/out of the cut, NOT on which ones. */ public class componenttesting3 { public Scanner sc; public PrintStream ps; /** * This class will keep track of a count and an amount, and will be sortable by the amount. * For example, if there are 150 engineers within a given Job Title who can handle 500 tests each, * then count=150 and amount=500 for that Job Title. */ public class CountAmount implements Comparable { public long count=0L; public long amount=0L; /** * Compare two of these things by amount */ public int compareTo( CountAmount c ) { return Long.compare( amount, c.amount ); } } // Component Types public CountAmount comptypes[] = new CountAmount[10000]; // Job Titles public CountAmount jobtitles[] = new CountAmount[10000]; /** * Driver. * @throws Exception */ public void doit() throws Exception { sc = new Scanner( System.in ); //new File( "componenttesting.in" ) ); ps = System.out; //new PrintStream( new FileOutputStream( "componenttesting.out" ) ); // Create all of these up front, to save some object creation time // on each data set. for( int i=0; i<10000; i++ ) { comptypes[i] = new CountAmount(); jobtitles[i] = new CountAmount(); } for(;;) { int n = sc.nextInt(); int m = sc.nextInt(); if( n==0 && m==0 ) break; // Total number of components long totalcomps = 0L; // Total number of tests across all components long totalneeds = 0L; // Read Component Types for( int i=0; i=0 && cEngineer edge out of the cut ++eout; // Here's the change in the cut cut -= jobtitles[j].amount; cut += cout; // Go through the Component Types in order. // Only put Component->Sink edges in the cut if they improve the cut. // We can so this in bulk, by Component Type, // rather than Component-by-Component. // Since eout is monotonically increasing, // c can be monotonically increasing. while( cSink edges for this Component Type into the cut. // This reduces the number of components out of the cut. cout -= comptypes[c].count; // Here's the change in the cut cut += comptypes[c].count * comptypes[c].amount; cut -= comptypes[c].count * eout; // Go to the next Component Type ++c; } // Is this the best cut we've seen so far? if( cut