I - Product Equality 解説 by en_translator
There are \(N^2\) ways to choose two variables and multiply them.
However, since each variable is enormous, if you naively perform multiplication using big integers (without caring overflows), a computational cost of about \(O(N^2 d \log d)\) is predicted (where \(d\) is the number of digits), which makes it difficult to solve the problem in this way.
For now, let us consider how to determine if three variables \(p,q\), and \(r\) satisfies \(p \times q = r\).
Here, we come up with somewhat daring criteria:
- Choose an integer \(x\). If \(p \times q \equiv r\) ( \(\rm{mod}\) \(x\) ), then determine that \(p \times q = r\).
This method fails when \((p \times q - r) \equiv 0\) ( \(\rm{mod}\) \(x\) ).
Here, consider choosing a prime greater than \(10^9\) as \(x\).
Then, the criteria misbehaves only for at most \(222\) primes \(x\) (because \(|p \times q - r| \le 10^{2000}\)), while there are tens of millions of primes between \(10^9\) and \(2 \times 10^9\).
Thus, the following randomized algorithm is a valid solution.
- Let \(S = \) a set of randomly chosen primes (between \(10^9\) and \(2 \times 10^9\)).
- If \(A_i \times A_j \equiv A_k\) for all elements \(x\) in \(S\), then determine that \(A_i \times A_j = A_k\).
- First evaluate \(A_k\) modulo \(x\) for all \(A_k\) and all elements \(x\) in \(S\), then determine if \(A_i \times A_j\) modulo \(x\) is contained in them.
This way, the problem can be solved in \(O(|S| \times N^2 \log N)\) time. While we do not precisely evaluate success rate in this editorial, it would be safe to choose \(|S|\) to be around \(20\).
Instead of determining if a picked \(x\) is a prime, you can just choose a random large integer. (It is very difficult to hack such a solution (prepare a test case for which such a solution misbehaves).
Sample code (C++):
#include<bits/stdc++.h>
using namespace std;
long long takemod(string &s,long long mod){
long long res=0;
for(auto &nx : s){
res*=10;res%=mod;
res+=(nx-'0');res%=mod;
}
return res;
}
int lb(vector<long long> &x,vector<vector<long long>> &lis){
int l=0,r=lis.size()-1;
while(l<=r){
int te=(l+r)/2;
if(lis[te] < x){l=te+1;}
else{r=te-1;}
}
return l;
}
int ub(vector<long long> &x,vector<vector<long long>> &lis){
int l=0,r=lis.size()-1;
while(l<=r){
int te=(l+r)/2;
if(lis[te] <= x){l=te+1;}
else{r=te-1;}
}
return l;
}
int main(){
std::random_device seed_gen;
std::mt19937_64 engine(seed_gen());
int n;
cin >> n;
int ss=20;
vector<long long> S;
for(int i=0;i<ss;i++){
S.push_back(1000000000+engine()%1000000000);
}
vector<vector<long long>> md;
for(int i=0;i<n;i++){
string a;
cin >> a;
vector<long long> cv;
for(int j=0;j<ss;j++){
cv.push_back(takemod(a,S[j]));
}
md.push_back(cv);
}
sort(md.begin(),md.end());
long long res=0;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
vector<long long> cp;
for(int k=0;k<ss;k++){
cp.push_back((md[i][k]*md[j][k])%S[k]);
}
res+=ub(cp,md)-lb(cp,md);
}
}
cout << res << "\n";
return 0;
}
投稿日時:
最終更新: