51nod 1318 最大公约数与最小公倍数方程组(2-SAT)

题意

给你 n 个元素,m 个方程。

每个方程形如

(1)gcd(xi,yi)=ci(2)lcm(xi,yi)=di

之类的形式。

询问这个方程组是否有解。有 T 组数据。

1T10,1n,m200

题解

这道题是一个很巧妙的 2SAT 。不会的话,可以参考 2-SAT 问题与解法小结

我们可以这样设计变量,令变量 a[i][j][k] 表示是否有 pj|xi ,上面限制就能表示出来啦。

一开始觉得每个质因子可以单独考虑,后来发现要一起考虑,因为别的 gcd,lcm 会限制这个的次数。

具体来说是这样的。

  1. gcd(xi,yi)=ci

    那么我们首先考虑 xi,yi 中与 ci 互质的质因子 p

    对于这些质因子 px,y 不能同时出现我们连一条 a[x][p][1]¬a[y][p][1] 的边(注意要连逆否命题的边)。

    那么我们接下来可以考虑,假设 ci 存在质因子 p 的最高次数为 k

    那么 xi,yi 两个数对于 p 的最低次数为 k ,且必有一个数次数刚好为 k ,那么连三条边就行了。

    首先强制使得 a[x][p][k],a[y][p][k] 为真。(也就是连一条从真到假的边就行了)

    然后如果 a[x][p][k+1] 为真,那么要使得 a[y][p][k+1] 为假。(逆否也要)

    这是因为不能存在两个次数都 k+1

  2. lcm(xi,yi)=di

    同样先考虑 xi,yi 中与 di 互质的质因子 p

    对于这些质因子 px,y 不能包含,所以强制使得 a[x][p][1],a[y][p][1] 为假。

    那么我们接下来可以考虑,假设 di 存在质因子 p 的最高次数为 k

    同上, xi,yi 两个数对于 p 的最高次数为 k ,且必有一个数次数刚好为 k ,那么连三条边就行了。

    强制使得 a[x][p][k+1],a[y][p][k+1] 为假。

    然后如果 a[x][p][k] 为假,那么要使得 a[y][p][k] 为真。(逆否也要)

连完这些,还要记得 a[x][p][k] 为真时,a[x][p][k1] 也要为真。

然后就可以轻松愉悦的码码码了。

然后对于 a[x][p][k] 标号的时候,可以用 std :: map<int, map<int, map<int, int> > > id 来实现qwq

STL 大法好!!!

复杂度是 O(Tmlog109) 的。

总结

对于 gcd,lcm 的题,可以对于指数进行考虑,就变成了高维的取 min 和取 max 问题。

代码

建议 学习一下我的代码

#include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << x << endl #define DEBUG(...) fprintf(stderr, __VA_ARGS__) #define fir first #define sec second using namespace std; typedef pair<int, int> PII; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() { int x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48); return x * fh; } void File() { #ifdef zjp_shadow freopen ("1318.in", "r", stdin); freopen ("1318.out", "w", stdout); #endif } const int N = 2e4 + 1e3; struct Two_Sat { int n; vector<int> G[N]; void Init(int n) { this -> n = n; For (i, 2, n << 1 | 1) G[i].clear(); } void Add(int x, int xv, int y, int yv) { x = x << 1 | xv; y = y << 1 | yv; G[x].push_back(y); G[y ^ 1].push_back(x ^ 1); } int sccno[N], scc_cnt, dfn[N], lowlink[N], sta[N], top, clk; void Tarjan(int u, int fa = 0) { dfn[u] = lowlink[u] = ++ clk; sta[++ top] = u; for (int v : G[u]) if (!dfn[v]) Tarjan(v, u), chkmin(lowlink[u], lowlink[v]); else if (!sccno[v]) chkmin(lowlink[u], dfn[v]); if (dfn[u] == lowlink[u]) { ++ scc_cnt; int now; do sccno[now = sta[top --]] = scc_cnt; while (u != now); } } bool Solve(int n) { this -> n = n; For (i, 2, n << 1 | 1) dfn[i] = sccno[i] = 0; scc_cnt = clk = 0; For (i, 2, n << 1 | 1) if (!dfn[i]) Tarjan(i); For (i, 1, n) if (sccno[i << 1] == sccno[i << 1 | 1]) return false; return true; } } T; int n, m; struct Equation { int x, y, val, opt; } lt[N]; set<int> fac[N]; void Get_Factor(int x, int val) { For (i, 2, sqrt(val + .5)) if (!(val % i)) { while (!(val % i)) val /= i; fac[x].insert(i); } if (val > 1) fac[x].insert(val); } int Size; map<int, map<int, map<int, int> > > id; int Get_Id(int x, int p, int k) { if (!id[p][x][k]) id[p][x][k] = ++ Size; return id[p][x][k]; } void Build_Again() { for (auto i : id) for (auto j : i.sec) { int Last = 0; for (auto k : j.sec) { if (Last) T.Add(k.sec, 1, Last, 1); Last = k.sec; } } id.clear(); } void Modify(int x, int val) { T.Add(x, val ^ 1, x, val); } void Resolve(int x, int y, int opt, int val) { set<int> fx = fac[x]; set<int> fy = fac[y]; int tmp = val; For (i, 2, sqrt(val + .5)) if (!(val % i)) { while (!(val % i)) val /= i; fx.erase(i); fy.erase(i); } if (val > 1) fx.erase(val), fy.erase(val); val = tmp; if (opt == 1) { for (auto prime : fx) Modify(Get_Id(x, prime, 1), 0); for (auto prime : fy) Modify(Get_Id(y, prime, 1), 0); } else { vector<int> V; set_union(fx.begin(), fx.end(), fy.begin(), fy.end(), inserter(V, V.begin())); for (int prime : V) { T.Add(Get_Id(x, prime, 1), 1, Get_Id(y, prime, 1), 0); T.Add(Get_Id(y, prime, 1), 1, Get_Id(x, prime, 1), 0); } } register int i = 2; while (val > 1) { if (!(val % i)) { int cnt = 1; while (!(val % i)) val /= i, ++ cnt; if (!opt) { Modify(Get_Id(x, i, cnt), 1); Modify(Get_Id(y, i, cnt), 1); T.Add(Get_Id(x, i, cnt + 1), 1, Get_Id(y, i, cnt + 1), 0); } else { Modify(Get_Id(x, i, cnt + 1), 0); Modify(Get_Id(y, i, cnt + 1), 0); T.Add(Get_Id(x, i, cnt), 0, Get_Id(y, i, cnt), 1); } } ++ i; if (i * i > val) i = val; } } int main () { File(); for (int cases = read(); cases; -- cases) { Size = 0; n = read(); m = read(); For (i, 1, n) fac[i].clear(); For (i, 1, m) { static char str[5]; scanf ("%s", str + 1); int opt = str[1] == 'L', x = read(), y = read(), val = read(); lt[i] = (Equation) {x, y, val, opt}; Get_Factor(x, val); Get_Factor(y, val); } For (i, 1, m) Resolve(lt[i].x, lt[i].y, lt[i].opt, lt[i].val); Build_Again(); puts(T.Solve(Size) ? "Solution exists" : "Solution does not exist"); T.Init(Size); } return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/9518319.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(543)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示