首先我们能注意到两个数x, y (0 < x , y < m) 乘以倍数互相可达当且仅当gcd(x, m) == gcd(y, m)
然后我们可以发现我们让gcd(x, m)从1开始出发走向它的倍数一个一个往里加元素就好啦, 往那边走
这个可以用dp求出来, dp[ i ] 表示 gcd(x, m)从 i 开始最大元素一共有多少个, dp[ i ] = max( dp[ j ] ) + cnt[ i ] 且 i | j
然后用扩展欧几里德求出走到下一步需要乘多少。
#include<bits/stdc++.h> #define LL long long #define fi first #define se second #define mk make_pair #define PLL pair<LL, LL> #define PLI pair<LL, int> #define PII pair<int, int> #define SZ(x) ((int)x.size()) #define ull unsigned long long using namespace std; const int N = 2e5 + 7; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const int mod = 1e9 + 7; const double eps = 1e-8; const double PI = acos(-1); LL n, m, dp[N], path[N]; bool ban[N]; vector<LL> gg[N]; vector<LL> ans; vector<LL> vc; LL x, y; LL exgcd(LL a, LL b, LL &x, LL &y) { if(!b) { x = 1; y = 0; return a; } else { LL gcd, t; gcd = exgcd(b, a % b, x, y); t = x; x = y; y = t - (a / b) * y; return gcd; } } LL dfs(LL x) { if(~dp[x]) return dp[x]; dp[x] = 0; for(int i = 2 * x; i < m; i += x) { LL cnt = dfs(i); if(cnt > dp[x]) { dp[x] = cnt; path[x] = i; } } dp[x] += SZ(gg[x]); return dp[x]; } LL solve(LL pre, LL to) { if(pre == -1) ans.push_back(to); else { LL gcd = exgcd(pre, -m, x, y); if(to % gcd) exit(0); x *= to / gcd; y *= to / gcd; LL mo = abs(m / gcd); x = ((x % mo) + mo) % mo; ans.push_back(x); } return to; } int main() { memset(dp, -1, sizeof(dp)); scanf("%lld%lld", &n, &m); for(int i = 1; i <= n; i++) { int x; scanf("%d", &x); ban[x] = true; } for(LL i = 1; i < m; i++) { if(ban[i]) continue; gg[__gcd(i, m)].push_back(i); } dfs(1); LL pre = -1; for(LL gcd = 1; gcd; gcd = path[gcd]) { for(int i = 0; i < SZ(gg[gcd]); i++) pre = solve(pre, gg[gcd][i]); } if(!ban[0]) ans.push_back(0); printf("%d\n", SZ(ans)); for(auto& t : ans) printf("%lld ", t); puts(""); return 0; } /* */