1. 程式人生 > 實用技巧 >洛谷 P3599 Koishi Loves Construction

洛谷 P3599 Koishi Loves Construction

思路

一道構造好題。

Task1

能夠看出兩點:

  • 第一個數必須是 \(n\)

    因為有 \(x+n\equiv x(\bmod n)\),如果第一個數不是 \(n\),必然會有兩個相鄰的字首和模 \(n\) 的值相等。

  • 除了 \(1\) 之外的奇數都不存在正確構造。

    \(n\) 為奇數時,有

    \[\sum\limits_{i=1}^{n-1}i\mod n=\dfrac{n\times(n-1)}{2}\mod n=0 \]

    因為已經確定了第一個數為 \(n\),所以一定會衝突。

剩下的 \(n-1\) 個數,可以找到一種為 \(\text{1 -2 3 -4 5...}\) 的方案,保證 \(1\sim n - 1\)

的所有數都能出現,因為這樣的話字首和在模 \(n\) 意義下獲得的數就是 \(1\sim n - 1\),然後負數要轉化為正數,最後的序列就能夠保證每個數只出現了一次。

程式碼裡的實現是先輸出第一個數 \(n\),然後輸出第 \(2\sim n\) 個數,如果 \(i\mod2 = 1\) 則輸出 \(n+1-i\),否則輸出 \(i-1\)

Task2

容易看出:

  • \(n\) 只能是最後一個數。

    \(n\) 不為最後一個數,那麼在 \(n\) 之後的字首積在模 \(n\) 意義下一定為 \(0\),必定會衝突。

  • \(1\) 只能是第一個數

    如果 \(1\) 不是第一個數,那麼就無法出現字首積模 \(n\)

    等於 \(1\) 的情況了,因此 \(1\) 必定是第一個數。

剩下的數,我們的目標是構造一個在模 \(n\) 意義下字首積連續遞增的序列。

那麼一種可行的構造方案是 \(1,\frac{2}{1},\frac{3}{2}...\frac{n}{n-1}\),因此我們只需要求出每個數的逆元,輸出即可。

但是會有無解的情況,那就是不為 \(4\) 的合數一定無解,因為如果 \(n\) 是合數,那麼一定存在小於 \(n\) 的兩個數乘積為 \(n\),所以只有 \(1,4\) 和所有的質數有解。

注意看特判 \(1\)\(4\) 的情況,因為 \(4\nmid 3!\)

程式碼

/*
  Name: P3599 Koishi Loves Construction
  Author: Loceaner
  Date: 08/09/20 11:24
  Description:
  Debug:
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;

const int A = 1e5 + 11;
const int B = 1e6 + 11;
const int inf = 0x3f3f3f3f;

inline int read() {
  char c = getchar();
  int x = 0, f = 1;
  for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
  for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
  return x * f;
}

int opt, T, inv[A];

inline bool judge(int x) {
  for (int i = 2; i * i <= x; i++) {
    if (x % i == 0) return 0;
  }
  return 1;
}

signed main() {
  opt = read(), T = read();
  while (T--) {
    int n = read();
    if (opt == 1) {
      if (n % 2 && n != 1) puts("0");
      else {
        cout << 2 << " ";
        cout << n << " ";
        for (int i = 2; i <= n; i++) {
          if (i % 2) cout << n + 1 - i << " ";
          else cout << i - 1 << " ";
        }
        puts("");
      }
    }
    if (opt == 2) {
      if (n == 1) {
        puts("2 1");
        continue;
      }
      if (n == 4) {
        puts("2 1 3 2 4");
        continue;
      }
      if (!judge(n)) puts("0");
      else {
        int mod = n;
        inv[0] = inv[1] = 1;
        for (int i = 2; i < n; i++) inv[i] = mod - (mod / i) * inv[mod % i] % mod;
        cout << 2 << " ";
        for (int i = 1; i < n; i++) cout << i * inv[i - 1] % mod << " ";
        cout << n << '\n';
      }
    }
  }
}