【51nod1318】最大公约数与最小公倍数方程组(2-SAT)(数论基础)

传送门


吐槽:

从各种意义上都难以形容的一道题。

好想到是好想,写起来真的。。。一言难尽。。。

(社论) 题解:

显然考虑分质因数来处理。

我们考虑每个数中当前处理的质因子的次数。

显然 g c d gcd gcd l c m lcm lcm此时就变成了一堆关于 min ⁡ , max ⁡ \min,\max min,max的限制。

对于每个数,显然次数不会超过 30 30 30,我们拆点来表示 x i x_i xi的次数,但是如果一个点 ( i , j ) (i,j) (i,j)表示 x i x_i xi中含有 j j j p p p的话,这显然就不是 2 − S A T 2-SAT 2SAT

考虑拆成两类点,其中 i d 1 [ i ] [ j ] id1[i][j] id1[i][j]表示 x i x_i xi p p p的次数 ≤ j \leq j j i d 2 [ i ] [ j ] id2[i][j] id2[i][j]表示 p p p的次数 > j > j >j

显然是 2 − S A T 2-SAT 2SAT

按照 g c d gcd gcd l c m lcm lcm的意义连边就行了。

对了,在 2 − S A T 2-SAT 2SAT中,如果需要强制选择 a a a,只需要从 a a a的反命题向 a a a连一条边就行了。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	static cs int Rlen=1<<22|1;
	static char buf[Rlen],*p1,*p2;
	
	inline char get_char(){
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline char peek(){
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1;
	}
	
	inline char get_sth(){
		while(isspace(peek()))gc();
		return gc();
	}
	
	template<typename T>
	inline T get(){
		char c;
		while(!isdigit(c=gc()));T num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int getint(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

namespace Graph{
	cs int N=2e4+4;
	
	int n;
	
	std::vector<int> G[N];
	
	inline void addedge(int u,int v){
		G[u].push_back(v);
	}
	
	int scc[N],scc_cnt;
	
	int low[N],dfn[N],clk;
	
	int st[N],top;
	bool inst[N];
	
	void tarjan(int u){
		low[u]=dfn[u]=++clk;
		st[++top]=u;inst[u]=true;
		for(int re v:G[u]){
			if(!dfn[v]){
				tarjan(v);
				low[u]=std::min(low[u],low[v]); 
			}
			else if(inst[v])low[u]=std::min(low[u],dfn[v]);
		}
		if(low[u]==dfn[u]){
			++scc_cnt;
			do{
				scc[u]=scc_cnt;
				u=st[top--];
				inst[u]=false;
			}while(low[u]!=dfn[u]);
		}
	}
	
	inline void init(int _n){
		n=_n;
		for(int re i=1;i<=n;++i)G[i].clear();
		memset(scc+1,0,sizeof(int)*n);
		memset(low+1,0,sizeof(int)*n);
		memset(dfn+1,0,sizeof(int)*n);
		clk=top=scc_cnt=0;
	}
	
	inline void work(){
		for(int re i=1;i<=n;++i)if(!dfn[i])tarjan(i);
	}
	
	inline bool iscon(int u,int v){return scc[u]==scc[v];}
}

namespace Solve{
	cs int P=1e5+5;
	std::vector<int> prime;
	bool mark[P];
	inline void linear_sieves(){
		for(int re i=2;i<P;++i){
			if(!mark[i])prime.push_back(i);
			for(int re j:prime){
				if(i*j>=P)break;
				mark[i*j]=true;
				if(i%j==0)break;
			}
		}
	}
	
	struct node{
		int u,v,val,typ;
		node(int _u,int _v,int _val,int _typ):u(_u),v(_v),val(_val),typ(_typ){}
	};
	
	cs int N=202,M=202;
	
	int n,m,cnt;
	int id1[N][35],id2[N][35];
	//id1[i][j] a_i 的p的指数 <=j
	//id2[i][j] a_i 的p的指数 > j  
	
	std::vector<node> vec;
	std::unordered_map<int,int> id;
	int p_cnt,factor[P];
	
	inline void clear(){
		vec.clear();
		id.clear();p_cnt=0;
	}
	
	inline void work(int val){
		for(int re i:prime){
			if((ll)i*i>=val)break;
			if(val%i==0){
				while(val%i==0)val/=i;
				if(id.find(i)==id.end()){
					id[i]=++p_cnt;
					factor[p_cnt]=i;
				}
			}
		}
		if(val>1){
			if(id.find(val)==id.end()){
				id[val]=++p_cnt;
				factor[p_cnt]=val;
			}
		}
	}
	
	int mn[N],mx[N];
	inline bool check(cs std::vector<node> &lim){
		int Mx=0;cnt=0;
		for(cs node &t:lim)Mx=std::max(Mx,t.val);
		for(int re u=1;u<=n;++u){
			mn[u]=0,mx[u]=Mx;
			for(int re j=0;j<=Mx;++j){
				id1[u][j]=++cnt;
				id2[u][j]=++cnt;
			}
		}
		Graph::init(cnt);using Graph::addedge;
		for(int re u=1;u<=n;++u){
			for(int re t=0;t<Mx;++t){
				addedge(id1[u][t],id1[u][t+1]);
				addedge(id2[u][t+1],id2[u][t]);
			}
		}
		for(cs node &t:lim){
			switch(t.typ){
				case 1:{//gcd
					int u=t.u,v=t.v,j=t.val;
					mn[u]=std::max(mn[u],t.val);
					mn[v]=std::max(mn[v],t.val);
					addedge(id2[u][j],id1[v][j]);
					addedge(id2[v][j],id1[u][j]);
					break;
				}
				case 0:{//lcm
					int u=t.u,v=t.v,j=t.val;
					mx[u]=std::min(mx[u],t.val);
					mx[v]=std::min(mx[v],t.val);
					if(j){
						addedge(id1[u][j-1],id2[v][j-1]);
						addedge(id1[v][j-1],id2[u][j-1]);
					}
					break;
				}
				default:assert(0);
			}
		}
		for(int re u=1;u<=n;++u){
			if(mn[u]>mx[u])return false;
			if(mn[u])addedge(id1[u][mn[u]-1],id2[u][mn[u]-1]);
			addedge(id2[u][mx[u]],id1[u][mx[u]]);
		}
		Graph::work();
		for(int re i=1;i<=n;++i){
			for(int re j=0;j<=Mx;++j)if(Graph::iscon(id1[i][j],id2[i][j]))return false;
		}
		return true;
	}
	
	inline void main(){
		n=getint(),m=getint();
		while(m--){
			char c=get_sth();
			int u=getint()+1,v=getint()+1,val=getint();
			work(val); 
			vec.push_back(node(u,v,val,c=='G'?1:0));
		}
		for(int re i=1;i<=p_cnt;++i){
			std::vector<node> lim=vec;
			int p=factor[i];
			for(node &t:lim){
				int power=0;
				while(t.val%p==0)t.val/=p,++power;
				t.val=power;
			}
			if(!check(lim)){
				puts("Solution does not exist");
				return ;
			}
		}
		puts("Solution exists");
	}
}

signed main(){
//	freopen("formula.in","r",stdin);freopen("formula.out","w",stdout);
	int T=getint();Solve::linear_sieves();
	while(T--)Solve::clear(),Solve::main();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值