题意

给你 \(n\) 个元素,\(m\) 个方程。

每个方程形如

\[\begin{align}
\gcd(x_i, y_i)=c_i\\
\mathrm{lcm}(x_i,y_i) = d_i
\end{align}
\]

之类的形式。

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

\(1 \le T \le 10, 1 \le n, m \le 200\) 。

题解

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

我们可以这样设计变量,令变量 \(a[i][j][k]\) 表示是否有 \(\displaystyle p^j | x_i\) ,上面限制就能表示出来啦。

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

具体来说是这样的。

  1. \(\gcd(x_i, y_i) = c_i\)

    那么我们首先考虑 \(x_i, y_i\) 中与 \(c_i\) 互质的质因子 \(p\) 。

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

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

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

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

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

    这是因为不能存在两个次数都 \(\ge k+1\) 。

  2. \(\mathrm{lcm} (x_i, y_i) = d_i\)

    同样先考虑 \(x_i, y_i\) 中与 \(d_i\) 互质的质因子 \(p\) 。

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

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

    同上, \(x_i, y_i\) 两个数对于 \(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][k - 1]\) 也要为真。

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

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

STL 大法好!!!

复杂度是 \(O(Tm \log 10^9)\) 的。

总结

对于 \(\gcd, \mathrm{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;
}

51nod 1318 最大公约数与最小公倍数方程组(2-SAT)的更多相关文章

  1. 求N个数的最大公约数和最小公倍数(转)

    除了分解质因数,还有另一种适用于求几个较小数的最大公约数.最小公倍数的方法 下面是数学证明及算法实现 令[a1,a2,..,an] 表示a1,a2,..,an的最小公倍数,(a1,a2,..,an)表 ...

  2. Java程序设计之最大公约数和最小公倍数

    题目:输入两个正整数number1和number2,求其最大公约数和最小公倍数. 算法:较大数和较小数取余,较小数除余数,一直到余数为0时,为最大公约数(辗转相除法):最大公倍数numbe1*numb ...

  3. 辗转相除法求最大公约数和最小公倍数【gcd】

    要求最小公倍数可先求出最大公约数 设要求两个数a,b的最大公约数 伪代码: int yushu,a,b: while(b不等于0) { yushu=a对b求余 b的值赋给a yushu的值赋给b } ...

  4. PAT - 基础 - 最大公约数和最小公倍数

    题目: 本题要求两个给定正整数的最大公约数和最小公倍数. 输入格式: 输入在一行中给出2个正整数M和N(<=1000). 输出格式: 在一行中顺序输出M和N的最大公约数和最小公倍数,两数字间以1 ...

  5. c 求两个整数的最大公约数和最小公倍数

    //求最大公约数是用辗转相除法,最小公倍数是根据公式 m,n 的 最大公约数* m,n最小公倍数 = m*n 来计算 #include<stdio.h> //将两个整数升序排列 void ...

  6. c语言求最大公约数和最小公倍数

    求最大公约数和最小公倍数 假设有两个数a和b,求a,b的最大公约数和最小公倍数实际上是一个问题,得出这两个数的最大公约数就可以算出它们的最小公倍数. 最小公倍数的公式是 a*b/m m为最大公约数 因 ...

  7. Java经典案例之-“最大公约数和最小公倍数”

    /** * 描述:输入两个正整数m和n,求其最大公约数和最小公倍数.(最大公约数:最大公约数, * 也称最大公因数.最大公因子,指两个或多个整数共有约数中最大的一个.) * (最小公倍数:几个数共有的 ...

  8. 求m和n的最大公约数和最小公倍数

    题目:输入两个正整数m和n,求其最大公约数和最小公倍数. 做这道题时,特意去查看了一下什么是最大公约数和最小公倍数. 后来直接去看了求解的思想,相信到企业中不会要求你闭门造车,若已有先例,可以研究之后 ...

  9. HDU 2503 a/b + c/d(最大公约数与最小公倍数,板子题)

    话不多说,日常一水题,水水更健康!┗|`O′|┛ 嗷~~ a/b + c/d Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768 ...

随机推荐

  1. zabbix server3.4 使用mailx配置邮件报警

    软件具体配置如下: 操作系统:Centos7.5 zabbix server版本:zabbix server3.4 zabbix agent版本:zabbix agent3.0 现在开始配置zabbi ...

  2. DVWA 黑客攻防演练(五)文件上传漏洞 File Upload

    说起文件上传漏洞 ,可谓是印象深刻.有次公司的网站突然访问不到了,同事去服务器看了一下.所有 webroot 文件夹下的所有文件都被重命名成其他文件,比如 jsp 文件变成 jsp.s ,以致于路径映 ...

  3. .net 支付宝接口小小误区

    1.该密匙目测不是私钥,应用官方文档生成的长私钥. 2. 此公钥用的是应用公钥 3.设置支付完成后的通知页面和回调页面 其他的按照官方文档的demo来实现即可

  4. Linux Logwatch的学习总结

    Logwatch功能介绍 Logwatch是一款Perl脚本编写的.开源的日志分析工具.它能对原始的日志文件进行解析并转换成结构化格式的文档,也能根据您的使用情况和需求来定制报告.Logwatch的特 ...

  5. Swift代理的使用

    Swift代理的使用 协议规定了用来实现某一特定功能所必需的方法和属性. 任意能够满足协议要求的类型被称为遵循(conform)这个协议. 类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议 ...

  6. C# groupby 应用小技巧

    这两天感冒了,导致大脑无法有效运转,一个朋友问我,groupby 怎么给list 分组,然后再将其中一个字段组合起来,恩,觉得很简单,结果才发现,自己的脑子真的是不够用了: 恩,其实是想写其他的,但是 ...

  7. 为Arch Linux更换Archlinuxcn源(清华源)

    上一篇随笔 archlinux切换官方中国源 里面写了如何切换到官方的中国源,但是因为那个源有一些软件并没有,特别是一些国人常用的中文软件,比如搜狗输入法等这些都是没有的.所以我们现在需要手动切换源一 ...

  8. windows下查看端口被占用及处理

    一.通过命令行查找端口被谁占用 1.window+R组合键,调出命令窗口 2.输入命令:netstat -ano,列出所有端口的情况.在列表中我们观察被占用的端口 3.查看被占用端口对应的PID,输入 ...

  9. .NET 术语

    .NET 术语 1. AOT 预编译器.与 JIT 类似,此编译器还可将 IL 转换为机器代码. 与 JIT 编译相比,AOT 编译在应用程序执行前进行并且通常在不同计算机上执行. 由于在运行时 AO ...

  10. LeetCode算法题-N-ary Tree Preorder Traversal(Java实现)

    这是悦乐书的第268次更新,第282篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第135题(顺位题号是589).给定一个n-ary树,返回其节点值的前序遍历.例如,给定 ...