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

Wesley13
• 阅读 606

题意

给你 $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;
}
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Jacquelyn38 Jacquelyn38
3年前
这些JS工具函数够你用到2020年底了
前言活不多说,自己平时搜集的干货函数奉上。干货函数找出数字在数组中下一个相邻的元素let i  "";let rr  ;const name  (n, arr1)    let num  Number(n);    for (let i  0; i < arr1.length; i)         const elemen
Stella981 Stella981
3年前
MSTP+VRRP+OSPF+双出口
拓扑图!MSTPVRRPOSPF双出口(https://s4.51cto.com/images/blog/202012/14/3e101c381bc712f9915994275649ac00.png?xossprocessimage/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFF
Wesley13 Wesley13
3年前
mysql简单常用语句汇总
1\.常用函数uuid和时间戳SELECTUUID(),UNIX_TIMESTAMP();将时间戳转为日期格式FROM_UNIXTIME(mw.created_at,'%Y%m%d%H:%i:%s')设置参数select@m_no:max(m_no)fromvc_m;set@m
Wesley13 Wesley13
3年前
mysql timestamp
 select from\_unixtime(m.createdAt, '%Y%m%d %H:%i:%s') from kfrobotaidlog m;select m.customeruid, from\_unixtime(m.createtime, '%Y%m%d %H:%i:%s') as \datetime\, m.kfui
Stella981 Stella981
3年前
LeetCode 5561. 获取生成数组中的最大值
文章目录1\.题目2\.解题1\.题目给你一个整数n。按下述规则生成一个长度为n1的数组nums:nums00nums11当2<2i<n时,nums2inumsi
Wesley13 Wesley13
3年前
MBR笔记
<bochs:100000000000e\WGUI\Simclientsize(0,0)!stretchedsize(640,480)!<bochs:2b0x7c00<bochs:3c00000003740i\BIOS\$Revision:1.166$$Date:2006/08/1117
Wesley13 Wesley13
3年前
5步实现数据指标增长,提升数据分析能力
!5步实现数据指标增长,提升数据分析能力(https://s4.51cto.com/images/blog/202009/15/081547a226ce35f8ee53d7e5675f38cb.png?xossprocessimage/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_10