結果
| 問題 |
No.2428 Returning Shuffle
|
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2023-08-19 05:18:29 |
| 言語 | D (dmd 2.109.1) |
| 結果 |
AC
|
| 実行時間 | 812 ms / 2,000 ms |
| コード長 | 5,008 bytes |
| コンパイル時間 | 4,487 ms |
| コンパイル使用メモリ | 179,948 KB |
| 実行使用メモリ | 93,312 KB |
| 最終ジャッジ日時 | 2024-11-28 18:59:50 |
| 合計ジャッジ時間 | 9,539 ms |
|
ジャッジサーバーID (参考情報) |
judge5 / judge3 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 3 |
| other | AC * 23 |
ソースコード
import std;
void main () {
int N, M; readln.read(N, M);
int[] T = new int[](M);
int[][] S = new int[][](M, 0);
foreach (i; 0..M) {
auto input = readln.split.to!(int[]);
T[i] = input[0];
S[i] = input[1..$];
S[i][] -= 1; // 0-indexed
}
solve(N, M, T, S);
}
void solve (int N, int M, int[] T, int[][] S) {
// 巡回置換の定義より、「元と変わった分」だけのループが必要なはず
// (最後に「元と変わっている」ということは、累積した巡回置換を一つにまとめたときに、要素数=(変わった数)の置換になるはず)
// あくまで予想。未証明。
// というわけで、一回シミュレートしてカウントする。
// 巡回置換の定義でのたうちまわってたけど、ようやくシミュレートできた。でもどうにもちがうっぽいな
// 見えた。「巡回」の連結成分みたいなものがあって、これのLCMをとればいいっていうことだろう
// また、各連結成分を見つけるのは比較的速いはず
struct pair {
int idx;
int val;
}
DList!pair Q;
int[] arr = new int[](N);
foreach (i; 0..N) {
arr[i] = i;
}
foreach (i; 0..M) {
if (S[i].length == 2) {
swap(arr[ S[i][0] ], arr[ S[i][1] ]);
} else {
foreach (j; 0..S[i].length) {
Q.insertBack(pair(S[i][ (j+1)%$ ], arr[ S[i][j] ]));
}
while (!Q.empty) {
auto head = Q.front; Q.removeFront;
arr[head.idx] = head.val;
}
}
}
int[] idx = new int[](N);
foreach (i, a; arr) {
idx[a] = cast(int) i;
}
// 連結成分のサイズを求める。
bool[] visited = new bool[](N);
int[] size;
foreach (i; 0..N) {
if (!visited[i]) {
int res = 0;
int begin = i;
while (true) {
if (visited[begin]) {
break;
}
begin = idx[begin];
visited[arr[begin]] = true;
res++;
}
size ~= res;
}
}
// test
// stderr.writeln(size);
// lcmの計算
// MOD上でのLCMはこのままだと計算できないようだ!(感嘆符がついているが、よく見れば gcd(ans, s) のところで破綻しているのは明らかだろう。頭が悪かった。)
// LCMは素因数分解をいい感じにすることでMODを途中に挟まずに直接計算できる!
// またこの使いにくい素因数分解を使うのか...()
long[] fact = new long[](N+1);
foreach (s; size) {
auto f = Factors(s);
foreach (i; 0..f.factor.length) {
fact[f.factor[i]] = max(fact[f.factor[i]], f.pow[i]);
}
}
const long MOD = 998244353;
long ans = 1;
foreach (i, f; fact) {
if (f != 0) {
ans *= modPow(i, f, MOD);
ans %= MOD;
}
}
writeln(ans);
}
void read(T...)(string S, ref T args) {
auto buf = S.split;
foreach (i, ref arg; args) {
arg = buf[i].to!(typeof(arg));
}
}
struct Factors {
long target;
long[] factor;
long[] pow;
bool is_prime () {
if (target <= 0) {
return false;
}
if (factor.length == 2 && pow[1] == 1) {
return true;
}
return false;
}
long[] combine_factor () {
if (target <= 0) {
return [];
}
long[] ret;
foreach (i, x; pow) {
foreach (k; 0..x) {
ret ~= factor[i];
}
}
return ret;
}
this (long target_) {
{ // check input
assert(0 < target_);
}
target = target_;
factor = [];
pow = [];
pow ~= 1;
factor ~= 1;
foreach (i; 2..target_) {
if (target_ < i*i) {
break;
}
if (target_ % i == 0) {
factor ~= i;
pow ~= 0;
while (target_ % i == 0) {
target_ /= i;
pow[$-1]++;
}
}
}
if (target_ != 1) {
factor ~= target_;
pow ~= 1;
}
}
}
long modPow (long a, long x, const int MOD) {
// assertion
assert(0 <= x);
assert(1 <= MOD);
// normalize
a %= MOD; a += MOD; a %= MOD;
// simple case
if (MOD == 1) {
return 0L;
}
if (x == 0) {
return 1L;
}
if (x == 1) {
return a;
}
// calculate
long res = 1L;
long base = a % MOD;
while (x != 0) {
if ((x&1) != 0) {
res *= base;
res %= MOD;
}
base = base*base; base %= MOD;
x >>= 1;
}
return res;
}