USACO 2022 February Contest, Platinum Problem 2. Sleeping in Class

USACO 2022 February Contest, Platinum Problem 2. Sleeping in Class

Bessie the cow was excited to recently return to in-person learning! Unfortunately, her instructor, Farmer John, is a very boring lecturer, and so she ends up falling asleep in class often.

Farmer John has noticed that Bessie has not been paying attention in class. He has asked another student in class, Elsie, to keep track of the number of times Bessie falls asleep in a given class. There are NN class periods (2N1052≤N≤105), and Elsie logs that Bessie fell asleep aiai times (1ai10181≤ai≤1018) in the ii-th class period. The total number of times Bessie fell asleep across all class periods is at most 10181018.

Elsie, feeling very competitive with Bessie, wants to make Farmer John feel like Bessie is consistently falling asleep the same number of times in every class -- making it appear that the issue is entirely Bessie's fault, with no dependence on Farmer John's sometimes-boring lectures.

The only ways Elsie may modify the log are by combining two adjacent class periods or splitting a class period into two. For example, if a=[1,2,3,4,5],a=[1,2,3,4,5], then if Elsie combines the second and third class periods the log will become [1,5,4,5][1,5,4,5]. If Elsie then chooses to split the third class period into two, the log can become any of [1,5,0,4,5][1,5,0,4,5][1,5,1,3,5][1,5,1,3,5][1,5,2,2,5][1,5,2,2,5][1,5,3,1,5][1,5,3,1,5], or [1,5,4,0,5][1,5,4,0,5].

Given QQ (1Q1051≤Q≤105) candidates q1,,qQq1,…,qQ for Bessie's least favorite number (1qi10181≤qi≤1018), for each of them help Elsie compute the minimum number of modifications to the log that she needs to perform so that all the numbers in the log become the same.

 

INPUT FORMAT (input arrives from the terminal / stdin):

The first line of each test case contains NN, and the second contains a1,a2,,aNa1,a2,…,aN. The third contains QQ, followed by QQ lines each containing an integer qiqi, a candidate for Bessie's least favorite number.

 

OUTPUT FORMAT (print output to the terminal / stdout):

For each qiqi, compute the minimum number of modifications required for Elsie to convert every entry of the log into qiqi, or 1−1 if it is impossible.

 

SAMPLE INPUT:

6
1 2 3 1 1 4
7
1
2
3
4
5
6
12

SAMPLE OUTPUT:

6
6
4
5
-1
4
5

Elsie needs at least four modifications to convert the log into all 3s.

 

   1 2 3 1 1 4
-> 3 3 1 1 4
-> 3 3 1 5
-> 3 3 6
-> 3 3 3 3

It is impossible for Elsie to convert the log into all 5s, which is why the correct output for that candidate is 1−1.

SCORING:

  • In test cases 2-4, N,Q5000N,Q≤5000
  • In test cases 5-7, all aiai are at most 109109.
  • Test cases 8-26 satisfy no additional constraints.

Problem credits: Jesse Choe and Benjamin Qi

USACO 2022 February Contest, Platinum Problem 2. Sleeping in Class 题解(翰林国际教育提供,仅供参考)

题解请注册登录查看

[/hide]

(Analysis by Daniel Zhang, Benjamin Qi)

Partial Credit: N,Q5000N,Q≤5000

To solve this subtask, we need to be able to answer each query in O(N)O(N) time. Define S=a1+a2++aNS=a1+a2+⋯+aN, and suppose that the candidate we are considering is dd.

 

  1. If dSd∤S, then the answer is 1−1.
  2. Otherwise, define pi=ij=1ajpi=∑j=1iaj for 1iN1≤i≤N to be the ii-th prefix sum of the input sequence. Consider how the set of prefix sums changes with each operation. Splitting a class period inserts a number into the set of prefix sums, while merging two class periods removes a number from the set of prefix sums. The set of prefix sums is initially all pipi, and at the end is all multiples of dd up to SS. Hence, the minimum number of operations is the size of the symmetric difference between the two sets. Letting xd=(# of i such that d|pi)xd=(# of i such that d|pi) be the number of prefix sums in common between the initial and final sequences, the size of the symmetric difference is N+Sd2xdN+Sd−2xd

This immediately leads to an O(NQ)O(NQ) solution.

Danny Mittal's code:

 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
 
public class SleepingInClassHarder {
 
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(in.readLine());
        long[] times = new long[n];
        long sum = 0;
        StringTokenizer tokenizer = new StringTokenizer(in.readLine());
        for (int j = 0; j < n; j++) {
            times[j] = Long.parseLong(tokenizer.nextToken());
            sum += times[j];
        }
        StringBuilder out = new StringBuilder();
        for (int q = Integer.parseInt(in.readLine()); q > 0; q--) {
            long targetNumber = Long.parseLong(in.readLine());
            if (sum % targetNumber == 0L) {
                long res = ((long) n) + (sum / targetNumber);
                long curr = 0;
                for (long time : times) {
                    curr += time;
                    if (curr % targetNumber == 0L) {
                        res -= 2L;
                    }
                }
                out.append(res);
            } else {
                out.append(-1);
            }
            out.append('\n');
        }
        System.out.print(out);
    }
}

Full Solution: We need a faster way to compute xdxd.

Define qi=gcd(pi,S)qi=gcd(pi,S). If d|Sd|S, then d|pid|qid|pi⇔d|qi, so we can ignore all the pipi and instead count the number of ii such that d|qid|qi. Each qiqi is a factor of SS. Knowing the number of times each factor occurs is enough to answer all queries, so we first count them.

Let σ0(S)σ0(S) denote the number of factors of SS. The maximum number of factors that can occur within the input constraints is achieved by the largest highly composite number not exceeding 10181018. This is

 

897612484786617600=28×34×52×72×11×13×17×19×23×29×31×37897612484786617600=28×34×52×72×11×13×17×19×23×29×31×37

which has 103680103680 factors (see OEIS).

In the worst case, there are O(σ0(S))O(σ0(S)) distinct nontrivial queries. If we can answer each query naively in O(σ0(S))O(σ0(S)), this part by itself will take O(σ0(S)2)O(σ0(S)2), which is too slow.

A faster way is to observe that if we arrange the factors in a multidimensional array, with a dimension for each prime factor, answering the queries from the frequencies is just computing multidimensional prefix sums.

Using inclusion-exclusion, we can compute each prefix sum in O(2ω(S))O(2ω(S)), where ω(S)ω(S) counts the number of distinct prime factors of SS (not to be confused with the ωω from complexity theory), but this is still slow.

A better way is to iterate over the dimensions, and compute prefix sums along that direction. This is a generalization of what is commonly referred to as Sum Over Subsets (SOS) and runs in O(ω(S)σ0(S))O(ω(S)σ0(S)).

To compute the dimensions of the array, it helps to know the prime factorization of SS. One way to do this is to use a factorization algorithm that is fast enough to factor numbers up to 10181018. Alternatively, one can use trial division up to 106106 to eliminate all but at most two prime factors, then compute gcdgcds of the remaining factor with the prefix sums and the queries. It is possible that a semiprime is left unfactored, but treating it as prime will not affect the correctness of the queries in that case since no query could distinguish it from being prime.

 

#include <cstdio>
#include <vector>
#include <numeric>

int N;
long long prefix[100005];
int Q;
long long queries[100005];

std::vector<std::pair<long long,int> > primes;

int num_factors(){
  int cnt=1;
  for(int i=0;i<primes.size();i++){
    cnt*=primes[i].second+1;
  }
  return cnt;
}

int freq[103680];

int& get_freq(long long num){
  int code=0;
  for(int j=0;j<primes.size();j++){
    int cnt=0;
    while(num%primes[j].first==0){
      num/=primes[j].first;
      cnt++;
    }
    code=code*(primes[j].second+1)+std::min(cnt,primes[j].second);
  }
  return freq[code];
}

int main(){
  scanf("%d",&N);
  for(int i=1;i<=N;i++){
    scanf("%lld",&prefix[i]);
    prefix[i]+=prefix[i-1];
  }
  long long rest=prefix[N];
  for(int p=2;p<=1000000;p++){
    if(rest%p==0){
      int cnt=0;
      while(rest%p==0){
        rest/=p;
        cnt++;
      }
      primes.emplace_back(p,cnt);
    }
  }
  //now rest has at most two prime factors
  for(int i=1;i<N;i++){
    long long tmp=std::gcd(rest,prefix[i]);
    if(tmp!=1&&tmp!=rest){
      if(tmp>rest/tmp){
        tmp=rest/tmp;
      }
      if(tmp*tmp==rest){
        primes.emplace_back(tmp,2);
      }else{
        primes.emplace_back(tmp,1);
        primes.emplace_back(rest/tmp,1);
      }
      rest=1;
    }
  }
  scanf("%d",&Q);
  for(int i=0;i<Q;i++){
    scanf("%lld",&queries[i]);
    long long tmp=queries[i];
    if(rest%tmp==0&&tmp!=1&&tmp!=rest){
      if(tmp>rest/tmp){
        tmp=rest/tmp;
      }
      if(tmp*tmp==rest){
        primes.emplace_back(tmp,2);
      }else{
        primes.emplace_back(tmp,1);
        primes.emplace_back(rest/tmp,1);
      }
      rest=1;
    }
  }
  if(rest!=1){
    //assume it is prime; can't tell anyway
    primes.emplace_back(rest,1);
  }
  for(int i=1;i<=N;i++){
    get_freq(prefix[i])++;
  }
  int block=1;
  for(int i=primes.size()-1;i>=0;i--){
    for(int code=num_factors()-1;code>=0;code--){
      if(code/block%(primes[i].second+1)!=0){
        freq[code-block]+=freq[code];
      }
    }
    block*=primes[i].second+1;
  }
  for(int i=0;i<Q;i++){
    if(prefix[N]%queries[i]!=0){
      printf("-1\n");
    }else{
      long long ans=N+prefix[N]/queries[i];
      ans-=get_freq(queries[i])*2;
      printf("%lld\n",ans);
    }
  }
}

In the implementation above, each factor ff of SS gets encoded to a location code(f)code(f) in the freqfreq array. This encoding is generalized to other numbers gg by mapping them to the location code(gcd(S,g))code(gcd(S,g)).

The part that performs the multidimensional prefix sum is as follows:

 

int block=1;
for(int i=primes.size()-1;i>=0;i--){
  for(int code=num_factors()-1;code>=0;code--){
    if(code/block%(primes[i].second+1)!=0){
      freq[code-block]+=freq[code];
    }
  }
  block*=primes[i].second+1;
}

 

  • Initially, freq[code(f)]freq[code(f)] stores the number of qiqi that equal ff (equivalently, the number of pipi such that gcd(pi,S)=fgcd(pi,S)=f).
  • After processing some primes, freq[code(f)]freq[code(f)] will store the number of ii such that qifqif only contains prime factors among the already processed primes.
  • After processing all primes, freq[code(f)]=xffreq[code(f)]=xf: the number of qiqi such that qiqi is a multiple of ff (equivalently, the number of pipi that is a multiple of ff).

[/hide]

翰林国际教育资讯二维码