UVa 11191 Problem F Perfect Square
UVa 11191 Problem F Perfect Square
http://uva.onlinejudge.org/external/111/11191.html
問題概要
n(n<200000)個の数字(abs(n) < 10^18)が与えられる。
それらの数字はすべて素因数の大きさが30以下である。
それらの数字を2個選んだとき、その積が整数の平方となる数字の選び方を答えよ。
また、3個選んだときの数も求めよ。
解法
数字をどの素因数が奇数回でてるか偶数回でてるかで分類する。
さらに、正か負で分類する。
それをbitで表し、それぞれの分類について何個入力データで来たか記録する。
1bit目を正負、2bit目を素因数の2の偶奇、3bit目を3の....としていくと、
11bitで全て分類できる。
掛け算することは、それらのbitのxorをとることになる。
xorをとった結果が0になれば、それはなにかの数の二乗である。(素因数がすべて偶数であることなので)
2個選ぶときは、自分自身をxorとれば0なるし、
3個選ぶ時はとりあえずなにか2個選んで、それらのxorをとった結果に
分類される数字をかければ、0になる。
基本的な方針はこれでok.
0が入力にふくまれるので、0はなにを書けても0なことから、例外処理が必要。
また、bitで分類した結果、それが0であらわされるような数字は少し別処理が必要。
そこらへんは頑張る。
ソース
#include<iostream> #define REP(i,b,n) for(int i=b;i<(int)n;i++) #define rep(i,n) REP(i,0,n) using namespace std; typedef long long lli; const int baseP[] = {-1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29}; const int P = 11; lli bitP(lli tmp){ int ret = 0; if(tmp < 0){ ret = 1; tmp *= -1; } REP(i, 1, P){ bool b = false; while(tmp % baseP[i] == 0){ b = !b; tmp /= baseP[i]; } if(b){ ret += (1 << i); } } return ret; } lli calcX(int n, int z){ if(z == 0)return 0; if(n < 2)return 0; if(n - z < 2)return (lli)n*(n-1)/2; return ((lli)n * (n-1)/2 - (lli)(n-z) * (n-z-1)/2); } lli calcY(int n, int z){ if(z == 0)return 0; if(n < 3)return 0; if(n-z<3){ return (lli)n * (n-1) * (n-2)/6; } return ((lli)n * (n-1) * (n-2)/6 - (lli)(n-z) * (n-z-1) * (n-z-2)/6); } int main(){ int T; cin >> T; rep(tc, T){ lli n, zeroCnt = 0, data[(1<<P)]; rep(i, (1<<P)){ data[i] = 0; } cin >> n; rep(i, n){ lli tmp; cin >> tmp; if(tmp == 0){ zeroCnt++; } else { data[bitP(tmp)]++; } } lli X = calcX(n, zeroCnt), Y = calcY(n, zeroCnt); if(data[0] > 0)X += ((lli)data[0] * (data[0]-1) )/2; if(data[0] > 2)Y += ((lli)data[0] * (data[0]-1) * (data[0]-2) )/6; REP(i, 1, (1<<P)){ if(data[i] == 0)continue; X += ((lli)data[i] * (data[i]-1))/2; Y += ((lli)data[i] * (data[i]-1))/2 * data[0]; REP(j, i+1, (1<<P)){ if((i^j) <= j)continue; Y += (lli)data[i] * data[j] * data[i^j]; } } cout << X << ' ' << Y << endl; } return 0; }