51nod 1318 最大公约数与最小公倍数方程组 2-SAT+数学

来源:互联网 发布:sat 句子合并 知乎 编辑:程序博客网 时间:2024/06/15 17:47

题意

求解一个有趣的方程组,方程组有N个未知正整数,x[0],x[1],x[2],…,x[N-1]。
方程组由M个方程组成,方程只有两种类型:
1)GCD(x[i],x[j]) = G ;其中,i != j,且GCD(a,b)为正整数a与b的最大公约数的值
2)LCM(x[i],x[j]) = L ;其中,i != j,且LCM(c,d)为正整数c与d的最小公倍数的值
你需要判断这样一个方程组是否存在解,且N个未知数都是正整数。
问题包含多组测试数据。
输入数据的第一行是一个整数T,表示有T组测试数据,1<=T<=10.
之后有T组相同结构的数据。
每组数据的第一行有两个整数N、M,分别表示未知数个数与方程个数,其中1<=N<=200,1<=M<=200.
之后M行,每行表示一个方程。
第i个方程格式为:”ti ai bi ci”,其中,ti为一个字符,ai,bi为[0,N-1]上的整数,1<=ci<=1,000,000,000。
如果ti=’L’,表示该方程为 LCM(x[ai],x[bi])=ci;
如果ti=’G’,表示该方程为 GCD(x[ai],x[bi])=ci。

分析

这题是真的难打。。。
思路其实并不难。我们对每一种素数分开处理。设当前处理到素数p,p在x中的指数为p1,在y中的指数为p2,LCM(x,y)则表示max(p1,p2)=c,GCD(x,y)则表示min(p1,p2)=c。
可以把每个变量拆成k个点对,其中的第i个点对(xi,yi),xi表示该变量选p^i,yi则表示不选。2-SAT建图之后跑一下即可。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;const int N=205;int n,m,zhi,tot,tot_block,dfn[N*N],low[N*N],stack[N*N],top,bel[N*N],last[N*N],prime[N*N],tim,cnt,mx;bool del[N*N],ins[N*N],val[N*N],vis[N*N];struct edge{int to,next;}e[N*N*N];struct data{int op,a,b,c;}pro[N];int read(){    int x=0,f=1;char ch=getchar();    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}void divi(int n){    for (int i=2;i*i<=n;i++)        if (n%i==0)        {            prime[++tot]=i;            while (n%i==0) n/=i;        }    if (n>1) prime[++tot]=n;}int point(int x,int y,int z){    return ((x-1)*(zhi+1)+y+1)*2-z;}void addedge(int u,int v){    //if (del[u]||del[v]) return;    e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;}void tarjan(int x){    dfn[x]=low[x]=++tim;    stack[++top]=x;ins[x]=1;    for (int i=last[x];i;i=e[i].next)        if (!dfn[e[i].to]&&!del[e[i].to]) tarjan(e[i].to),low[x]=min(low[x],low[e[i].to]);        else if (ins[e[i].to]&&!del[e[i].to]) low[x]=min(low[x],dfn[e[i].to]);    if (dfn[x]==low[x])    {        tot_block++;int y;        while (y!=x)        {            y=stack[top];top--;            ins[y]=0;bel[y]=tot_block;        }    }}bool check(int pri){    zhi=0;int tmp=mx;    while (tmp>=pri) zhi++,tmp/=pri;    int s=point(n,zhi,0);    for (int i=1;i<=s;i++) last[i]=del[i]=dfn[i]=low[i]=0;cnt=tim=tot_block=0;    for (int i=1;i<=n;i++)        for (int j=0;j<=zhi;j++)            for (int k=0;k<=zhi;k++)                if (j!=k) addedge(point(i,j,1),point(i,k,0));    for (int i=1;i<=m;i++)    {        int x=pro[i].a,y=pro[i].b,z=0,tmp=pro[i].c;        while (tmp%pri==0) tmp/=pri,z++;        if (del[point(x,z,0)]&&del[point(y,z,0)]) return 0;        if (!pro[i].op)        {            for (int j=0;j<z;j++) del[point(x,j,0)]=del[point(x,j,1)]=del[point(y,j,0)]=del[point(y,j,1)]=1;            for (int j=z+1;j<=zhi;j++) addedge(point(x,j,1),point(y,z,1)),addedge(point(y,j,1),point(x,z,1));        }        else        {            for (int j=z+1;j<=zhi;j++) del[point(x,j,0)]=del[point(x,j,1)]=del[point(y,j,0)]=del[point(y,j,1)]=1;            for (int j=0;j<z;j++) addedge(point(x,j,1),point(y,z,1)),addedge(point(y,j,1),point(x,z,1));        }    }    for (int i=1;i<=s;i++) if (!dfn[i]&&!del[i]) tarjan(i);    for (int i=1;i<=tot_block;i++) val[i]=0;    for (int i=1;i<=s;i++)        if (!del[i])            for (int j=last[i];j;j=e[j].next) if (del[e[j].to]&&(e[j].to&1)) {val[bel[i]]=1;break;}    for (int i=1;i<=n;i++)    {        for (int j=0;j<=zhi;j++)        {            if (del[point(i,j,1)]) continue;            if (bel[point(i,j,1)]==bel[point(i,j,0)]) val[bel[point(i,j,0)]]=1;            if (vis[bel[point(i,j,1)]]) val[bel[point(i,j,1)]]=1;            else vis[bel[point(i,j,1)]]=1;        }        for (int j=0;j<=zhi;j++) vis[bel[point(i,j,1)]]=0;    }    for (int i=1;i<=n;i++)    {        int flag=0;        for (int j=0;j<=zhi;j++)        {            if (del[point(i,j,0)]||val[bel[point(i,j,1)]]) continue;            flag=1;break;        }        if (!flag) return 0;    }    return 1;}int main(){    int T=read();    while (T--)    {        n=read();m=read();mx=0;        for (int i=1;i<=m;i++)        {            char ch[2];scanf("%s",ch);            pro[i].a=read()+1;pro[i].b=read()+1;pro[i].op=ch[0]=='G'?0:1;            divi(pro[i].c=read());mx=max(mx,pro[i].c);        }        sort(prime+1,prime+tot+1);tot=unique(prime+1,prime+tot+1)-prime-1;        int flag=0;        for (int i=1;i<=tot;i++) if (!check(prime[i])) {flag=1;break;}        if (!flag) puts("Solution exists");        else puts("Solution does not exist");    }    return 0;}
原创粉丝点击