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


    题意

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

    每个方程形如

    [egin{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] o eg 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;
    }
    
  • 相关阅读:
    洛谷P1070 道路游戏
    洛谷P1556 幸福的路
    洛谷P1457 城堡 The Castle
    洛谷P1298 最接近的分数
    2017-9-13 NOIP模拟赛[xxy]
    洛谷P3405 [USACO16DEC]Cities and States省市
    洛谷P1549 棋盘问题(2)
    洛谷P1578 奶牛浴场
    洛谷P2073 送花
    洛谷P3797 妖梦斩木棒
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/9518319.html
Copyright © 2020-2023  润新知