結果
| 問題 | No.3441 Sort Permutation 2 |
| コンテスト | |
| ユーザー |
startcpp
|
| 提出日時 | 2026-02-08 13:13:10 |
| 言語 | C++14 (gcc 15.2.0 + boost 1.89.0) |
| 結果 |
AC
|
| 実行時間 | 373 ms / 2,000 ms |
| コード長 | 2,390 bytes |
| 記録 | |
| コンパイル時間 | 4,674 ms |
| コンパイル使用メモリ | 98,472 KB |
| 実行使用メモリ | 18,040 KB |
| 最終ジャッジ日時 | 2026-02-08 13:13:31 |
| 合計ジャッジ時間 | 20,466 ms |
|
ジャッジサーバーID (参考情報) |
judge1 / judge5 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 3 |
| other | AC * 41 |
ソースコード
// [2点swapと操作回数]
// i->P_iのグラフを考える。
// i,jを選ぶと、i->P_j, j->P_i に行き先が入れ替わる。それ以外は変わらない。(隣接swap以外なら{i, j}と{P_i, P_j}が排反なため。隣接swapは別途確認。)
// i,jを同じサイクルから選ぶとサイクルが分かれる。異なるサイクルから選ぶとサイクルが繋がる。
// 操作回数を最小化するには、i,jを同じサイクルから選び続ける必要がある。また、それで十分。
// ↑この辺の話は、適当な順列(何でもいいが、例えば P = (2 3 4 5 6 1))で実際に試すと予想がつきやすい。定番の考察なので覚えてもよさそう。
//
// [余り/GCDなどを考える]
// 同じサイクルのなかの mod k が全て一致していれば、サイクルに含まれる頂点数 - 1 だけのダメージが確定する。
// 一致していなければ、好きな値を1つpopできる (自己ループにできる) ので、最後まで「mod kが2種類以上の状態」を保てて、ノーダメージにできる。
//
// [k=1,2,…,Nに対応する]
// サイクルの最小値が0になるよう値をシフトすると、「すべての頂点がkの倍数なら、ans_kに+C-1を寄与」とできる。(C=サイクルに含まれる頂点数)
// サイクルの値の最大公約数をgとするとき、gの「約数」であるようなkについて加算を行えばよい。
// a[g] += C-1 などとしておき、ans[k] = a[k] + a[2k] + a[3k] + ... とすればOK。調和級数でO(NlogN)に抑えられる。
// なお、愚直に加算をして時間のかかるケースがあるのかは…定かではない。
#include <iostream>
#include <atcoder/dsu>
#define rep(i, n) for(i = 0; i < n; i++)
using namespace std;
using namespace atcoder;
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
int n;
int p[200000];
int main() {
int i;
cin >> n;
rep(i, n) { cin >> p[i]; p[i]--; }
dsu uf(n);
rep(i, n) uf.merge(i, p[i]);
vector<int> a(n);
for (auto gr: uf.groups()) {
int minV = 1e+9;
for (int v: gr) minV = min(minV, v);
int g = 0;
for (int v: gr) g = gcd(g, v - minV);
a[g] += (int)gr.size() - 1;
}
for (i = 1; i < n; i++) {
int ans = 0;
for (int j = i; j < n; j += i) ans += a[j];
cout << ans << endl;
}
return 0;
}
startcpp