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

21 篇文章 0 订阅
4 篇文章 0 订阅

原题链接.

题解:

首先分解质因数,gcd就相当于指数的min,lcm就相当于指数的max。

于是问题变成了这样:
给出一坨类似于以下的限制:
min(a,b) = c
max(a,b) = c
问是否有解?

以min为例.

min(a,b) = c
即(a >= c) and (b >= c) and (a = c or b = c)

注意到指数是很小的,c不超过三十。

于是可以把x拆成31(0->30)个组,每个点再拆2个, xi 表示x<=i的布尔值, xi 就是x>i的布尔值,这两个刚好必须选一个。

xi 一定要向 xi+1 连边, xi 也一定要向 xi1 连边。
a>=c,所以 ai -> ac1(0<=i<c)
b>=c,同理。
a = c or b = c,因为已经有了a>=c and b >= c,可以看成a <= c or b <= c,
所以 ai -> bc(i>c) , bi -> ac(i>c)

然后跑过Trjan判断一下有没有 xi xi 在一个强联通分量里即可。

Code:

#include<set>
#include<cstdio>
#include<algorithm>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;

const int N = 1e6;

bool bz[N + 5]; int p[N];

char S[N];

int T, n, m, a[N], b[N], c[N];

int u[50], v[50];

int next[N], to[N], final[N], tot;

int num[205][105][2], tt, mx;

void Shai() {
    fo(i, 2, N)  {
        if(!bz[i]) p[++ p[0]] = i;
        fo(j, 1, p[0]) {
            int k = i * p[j];
            if(k > N) break;
            bz[k] = 1;
            if(i % p[j] == 0) break;
        }
    }
}

void Fen(int x) {
    u[0] = 0;
    for(int i = 1; p[i] * p[i] <= x; i ++)
        if(x % p[i] == 0) {
            u[++ u[0]] = p[i], v[u[0]] = 0;
            while(x % p[i] == 0)
                x /= p[i], v[u[0]] ++;
        }
    if(x > 1) u[++ u[0]] = x, v[u[0]] = 1;
}

int Q_z(int x, int y) {
    int z = 0; while(x % y == 0) x /= y, z ++;
    return z;
}


set<int> s;

void Clear() {
    fo(i, 1, tot) next[i] = 0;
    fo(i, 1, tt) final[i] = 0;
    tot = 0;
}

void link(int x, int y) {
    next[++ tot] = final[x], to[tot] = y, final[x] = tot;
}

int z[N], dfn[N], low[N], ff[N], d[N], bd[N], td;


void dg(int x) {
    bd[x] = 1; d[++ d[0]] = x;
    dfn[x] = low[x] = ++ td;
    for(int i = final[x]; i; i = next[i])  {
        int y = to[i];
        if(!dfn[y]) dg(y), low[x] = min(low[x], low[y]); else
        if(bd[y]) low[x] = min(low[x], dfn[y]);
    }
    if(low[x] == dfn[x]) {
        for(; d[d[0]] != x; d[0] --)
            ff[d[d[0]]] = x, bd[d[d[0]]] = 0;
        d[0] --; ff[x] = x; bd[x] = 0;
    }
}


bool pd() {
    td = 0;
    fo(i, 1, tt) low[i] = 0, dfn[i] = 0;
    fo(i, 1, tt) if(!dfn[i])
        dg(i);
    fo(i, 1, n) fo(j, 0, mx) {
        if(ff[num[i][j][0]] == ff[num[i][j][1]]) {
            return 0;
        }
    }
    return 1;
}

char get() {
    char c = ' '; for(;c != 'G' && c != 'L'; c = getchar());
    return c;
}


int main() {
    Shai();
    for(scanf("%d", &T); T; T --) {
        s.clear();
        scanf("%d %d", &n, &m);
        fo(i, 1, m) {
            S[i] = get();
            scanf("%d %d %d", &a[i], &b[i], &c[i]);
            a[i] ++; b[i] ++;
            Fen(c[i]);
            fo(i, 1, u[0]) s.insert(u[i]);
        }
        int ans = 1;
        while(!s.empty()) {
            int x = *s.begin(); s.erase(x);
            mx = 0;
            fo(i, 1, m) z[i] = Q_z(c[i], x), mx = max(mx, z[i]);
            tt = 0; fo(i, 1, n) fo(j, 0, mx) num[i][j][0] = ++ tt, num[i][j][1] = ++ tt;
            fo(i, 1, n) fo(j, 0, mx - 1) link(num[i][j][0], num[i][j + 1][0]);
            fo(i, 1, n) fo(j, 1, mx) link(num[i][j][1], num[i][j - 1][1]);
            Clear();
            fo(i, 1, m) {
                if(S[i] == 'G') {
                    fo(j, 0, z[i] - 1) {
                        link(num[a[i]][j][0], num[a[i]][j][1]);
                        link(num[b[i]][j][0], num[b[i]][j][1]);
                    }
                    fo(j, z[i], mx) {
                        link(num[a[i]][j][1], num[b[i]][z[i]][0]);
                        link(num[b[i]][j][1], num[a[i]][z[i]][0]);
                    }
                } else {
                    fo(j, z[i], mx) {
                        link(num[a[i]][j][1], num[a[i]][j][0]);
                        link(num[b[i]][j][1], num[b[i]][j][0]);
                    }
                    fo(j, 0, z[i] - 1) {
                        link(num[a[i]][j][0], num[b[i]][z[i] - 1][1]);
                        link(num[b[i]][j][0], num[a[i]][z[i] - 1][1]);
                    }
                }
            }
            ans &= pd();
            if(!ans) break;
        }
        if(ans) printf("Solution exists\n"); else printf("Solution does not exist\n");
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值