結果
| 問題 |
No.1746 Sqrt Integer Segments
|
| ユーザー |
startcpp
|
| 提出日時 | 2021-11-18 00:43:07 |
| 言語 | C++14 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 666 ms / 2,000 ms |
| コード長 | 1,669 bytes |
| コンパイル時間 | 991 ms |
| コンパイル使用メモリ | 88,500 KB |
| 実行使用メモリ | 80,292 KB |
| 最終ジャッジ日時 | 2024-09-13 09:24:00 |
| 合計ジャッジ時間 | 17,675 ms |
|
ジャッジサーバーID (参考情報) |
judge1 / judge5 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 2 |
| other | AC * 28 |
ソースコード
//24 -> 2^3 * 3 -> 2 * 3のように置き換えてOK。
//各素因数の個数 mod 2の累積和を考えると、これをキーにして数えたいが、2 3 5 7 11 …のようなケースで膨らむ。
//集合をハッシュで管理できないか? -> ゾブリストハッシュ!
//今回のように、2回同じ要素が来ると元に戻る性質のときに使えるハッシュ。
//unsigned long longで乱数生成すれば、衝突の心配も少ない。
#include <cstdio>
#include <random>
#include <map>
#define rep(i, n) for(i = 0; i < n; i++)
using namespace std;
const int MAX = 1000000;
int n;
int a[200000];
bool isPrime[MAX + 1];
vector<int> ps[MAX + 1];
unsigned long long Rnd[MAX + 1];
mt19937_64 mt(2521);
void init() {
for (int i = 2; i <= MAX; i++) isPrime[i] = true;
for (int i = 2; i <= MAX; i++) {
if (isPrime[i]) {
for (int j = i * 2; j <= MAX; j += i) {
isPrime[j] = false;
int tmp = j;
int cnt = 0;
while (tmp % i == 0) {
tmp /= i;
cnt++;
}
if (cnt % 2 == 1) {
ps[j].push_back(i);
}
}
ps[i].push_back(i);
}
}
for (int i = 0; i <= MAX; i++) {
Rnd[i] = mt();
}
}
map<unsigned long long, int> cnts;
signed main() {
int i, j;
init();
scanf("%d", &n);
rep(i, n) scanf("%d", a + i);
unsigned long long zhash = 0;
cnts[zhash]++;
rep(i, n) {
rep(j, ps[a[i]].size()) {
int p = ps[a[i]][j];
zhash ^= Rnd[p];
}
cnts[zhash]++;
}
long long ans = 0;
for (map<unsigned long long, int>::iterator it = cnts.begin(); it != cnts.end(); it++) {
int cnt = it->second;
ans += (long long)cnt * (cnt - 1) / 2;
}
printf("%lld\n", ans);
return 0;
}
startcpp