51nod 1318 最大公约数与最小公倍数方程组(2-SAT)
题意
给你 \(n\) 个元素,\(m\) 个方程。
每个方程形如
\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\) 会限制这个的次数。
具体来说是这样的。
\(\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\) 。
\(\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)的更多相关文章
- 求N个数的最大公约数和最小公倍数(转)
除了分解质因数,还有另一种适用于求几个较小数的最大公约数.最小公倍数的方法 下面是数学证明及算法实现 令[a1,a2,..,an] 表示a1,a2,..,an的最小公倍数,(a1,a2,..,an)表 ...
- Java程序设计之最大公约数和最小公倍数
题目:输入两个正整数number1和number2,求其最大公约数和最小公倍数. 算法:较大数和较小数取余,较小数除余数,一直到余数为0时,为最大公约数(辗转相除法):最大公倍数numbe1*numb ...
- 辗转相除法求最大公约数和最小公倍数【gcd】
要求最小公倍数可先求出最大公约数 设要求两个数a,b的最大公约数 伪代码: int yushu,a,b: while(b不等于0) { yushu=a对b求余 b的值赋给a yushu的值赋给b } ...
- PAT - 基础 - 最大公约数和最小公倍数
题目: 本题要求两个给定正整数的最大公约数和最小公倍数. 输入格式: 输入在一行中给出2个正整数M和N(<=1000). 输出格式: 在一行中顺序输出M和N的最大公约数和最小公倍数,两数字间以1 ...
- c 求两个整数的最大公约数和最小公倍数
//求最大公约数是用辗转相除法,最小公倍数是根据公式 m,n 的 最大公约数* m,n最小公倍数 = m*n 来计算 #include<stdio.h> //将两个整数升序排列 void ...
- c语言求最大公约数和最小公倍数
求最大公约数和最小公倍数 假设有两个数a和b,求a,b的最大公约数和最小公倍数实际上是一个问题,得出这两个数的最大公约数就可以算出它们的最小公倍数. 最小公倍数的公式是 a*b/m m为最大公约数 因 ...
- Java经典案例之-“最大公约数和最小公倍数”
/** * 描述:输入两个正整数m和n,求其最大公约数和最小公倍数.(最大公约数:最大公约数, * 也称最大公因数.最大公因子,指两个或多个整数共有约数中最大的一个.) * (最小公倍数:几个数共有的 ...
- 求m和n的最大公约数和最小公倍数
题目:输入两个正整数m和n,求其最大公约数和最小公倍数. 做这道题时,特意去查看了一下什么是最大公约数和最小公倍数. 后来直接去看了求解的思想,相信到企业中不会要求你闭门造车,若已有先例,可以研究之后 ...
- HDU 2503 a/b + c/d(最大公约数与最小公倍数,板子题)
话不多说,日常一水题,水水更健康!┗|`O′|┛ 嗷~~ a/b + c/d Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768 ...
随机推荐
- zabbix server3.4 使用mailx配置邮件报警
软件具体配置如下: 操作系统:Centos7.5 zabbix server版本:zabbix server3.4 zabbix agent版本:zabbix agent3.0 现在开始配置zabbi ...
- DVWA 黑客攻防演练(五)文件上传漏洞 File Upload
说起文件上传漏洞 ,可谓是印象深刻.有次公司的网站突然访问不到了,同事去服务器看了一下.所有 webroot 文件夹下的所有文件都被重命名成其他文件,比如 jsp 文件变成 jsp.s ,以致于路径映 ...
- .net 支付宝接口小小误区
1.该密匙目测不是私钥,应用官方文档生成的长私钥. 2. 此公钥用的是应用公钥 3.设置支付完成后的通知页面和回调页面 其他的按照官方文档的demo来实现即可
- Linux Logwatch的学习总结
Logwatch功能介绍 Logwatch是一款Perl脚本编写的.开源的日志分析工具.它能对原始的日志文件进行解析并转换成结构化格式的文档,也能根据您的使用情况和需求来定制报告.Logwatch的特 ...
- Swift代理的使用
Swift代理的使用 协议规定了用来实现某一特定功能所必需的方法和属性. 任意能够满足协议要求的类型被称为遵循(conform)这个协议. 类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议 ...
- C# groupby 应用小技巧
这两天感冒了,导致大脑无法有效运转,一个朋友问我,groupby 怎么给list 分组,然后再将其中一个字段组合起来,恩,觉得很简单,结果才发现,自己的脑子真的是不够用了: 恩,其实是想写其他的,但是 ...
- 为Arch Linux更换Archlinuxcn源(清华源)
上一篇随笔 archlinux切换官方中国源 里面写了如何切换到官方的中国源,但是因为那个源有一些软件并没有,特别是一些国人常用的中文软件,比如搜狗输入法等这些都是没有的.所以我们现在需要手动切换源一 ...
- windows下查看端口被占用及处理
一.通过命令行查找端口被谁占用 1.window+R组合键,调出命令窗口 2.输入命令:netstat -ano,列出所有端口的情况.在列表中我们观察被占用的端口 3.查看被占用端口对应的PID,输入 ...
- .NET 术语
.NET 术语 1. AOT 预编译器.与 JIT 类似,此编译器还可将 IL 转换为机器代码. 与 JIT 编译相比,AOT 编译在应用程序执行前进行并且通常在不同计算机上执行. 由于在运行时 AO ...
- LeetCode算法题-N-ary Tree Preorder Traversal(Java实现)
这是悦乐书的第268次更新,第282篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第135题(顺位题号是589).给定一个n-ary树,返回其节点值的前序遍历.例如,给定 ...