题解:
总结几种做法,并指出其中的优劣;
主要是两种算法,一种是暴力dfs,另一种是并查集。
事实上这种“每条边都考虑”大概都可以用上述两种办法处理,并查集复杂度略高,但是其实近乎线性。
暴力dfs就是,建一张无向图(不能是有向的),然后对于每个联通块,第一个元素设为1,然后通过第一个元素,
算出其它元素的值,然后对于点x,以及边(x,y),如果y也访问过了,就判断一下通过x算的y和已经算出来的y是否相等即可。
并查集是类似的,因为是无向图,先加边加成树(或者森林),然后之后在加边就判断即可。
其实这题最主要的是精度问题。
但是出题人特别良心没有卡精度,double都水过去了。
精度控制的几种办法:
第一种,最好想的,直接上double(或者long double)。
问题:数据理论最大是100^1000的,理论上会爆(事实没有)。
第二种,比较容易想到的,取对数,变成加减运算。
问题:可以卡精度,例如1e18和(1e9-1)*(1e9+1)。但是也没有卡。
第三种,在模质数意义下进行运算,除法变成乘逆元,判断类似哈希。
问题:理论上会冲突,实际上不会(几乎不会)。
第四种,注意到x,y<=100可以质因数分解。
问题:这个应该能够保证正确,但是常数略大,(尽管对于这个题是足够的)。
代码:用的是第一种SB方法竟然没有被卡QwQ
//BZOJ 4602
//SDOI 2016
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define MAXN 1100
#define MAXM 21000
using namespace std;
struct edges{
int to,pre;
double wgt;
}e[MAXM];
int h[MAXN],etop;
int add_edge(int u,int v,double wgt)
{
etop++;
e[etop].to=v;
e[etop].pre=h[u];
e[etop].wgt=wgt;
h[u]=etop;
return 0;
}
double val[MAXN];
const double ep=0.000001;
bool vis[MAXN];
bool dcmp(double x)
{
if(fabs(x)<ep) return false;
else return true;
}
bool dfs(int x)
{
vis[x]=true;
for(int i=h[x];i;i=e[i].pre)
if(!vis[e[i].to])
{
val[e[i].to]=val[x]*e[i].wgt;
if(!dfs(e[i].to)) return false;
}
else if(dcmp(val[e[i].to]-val[x]*e[i].wgt)) return false;
return true;
}
int main()
{
int T;scanf("%d",&T);
for(int t=1;t<=T;t++)
{
int n,m;scanf("%d%d",&n,&m);
memset(h,0,sizeof(h));etop=0;
memset(vis,false,sizeof(vis));
for(int i=1;i<=m;i++)
{
int u,v,x,y;scanf("%d%d%d%d",&u,&v,&x,&y);
add_edge(u,v,(double)y/x);
add_edge(v,u,(double)x/y);
}
for(int i=1;i<=n;i++)
if(!vis[i])
{
val[i]=1.0;
if(!dfs(i))
{
printf("Case #%d: No\n",t);
goto loop;
}
}
printf("Case #%d: Yes\n",t);
loop:;
}
return 0;
}