题解 P4079 【[SDOI2016]齿轮】
看大佬们都说是并查集蒟蒻我真是瑟瑟发抖啊。。。
我写的是分数+gcd
首先假如说让人来处理这件事的话会采用一种什么样的思路呢?
如果是我的话首先会随便找一个点(齿轮)开始,设它的速度为+1
接下来按照给出的边走,一路用“分数”来表达每个齿轮的速度,当要前进的下一个点也被标记了速度,验证新旧两速度是否相同即可。这个思路可以说是简单暴力。。。
再来讨论下复杂度,首先是遍历,不难发现每条边顶多被正反走两边,所以是O(M),接下来是gcd,如果写的是更相减损法的话就会有稳定的log(100),就是个7左右的常数,总代价可以粗略的理解为100*M 幸好M比较小还是可以过的2333
遍历时要记得有可能不是张连通图
接下来请看看代码吧,希望对你有所帮助!
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int gcd(int a,int b){
if(a<b)swap(a,b);
int c;
while(b){c=a%b;a=b;b=c;}
return a;
}
struct frnum{
int s,x,y; //s:0->+ 1->-, x/y
}s[1050];
frnum operator *(frnum A,int b){
if(b<0){
A.s^=1;
b=-b;
}
A.x=A.x*b;
int w=gcd(A.x,A.y);
A.x/=w;
A.y/=w;
return A;
}
frnum operator /(frnum A,int b){
if(b<0){
A.s^=1;
b=-b;
}
A.y=A.y*b;
int w=gcd(A.x,A.y);
A.x/=w;
A.y/=w;
return A;
}
bool operator ==(frnum A,frnum B){
return A.s==B.s&&A.x==B.x&&A.y==B.y;
}
struct line{
int nxt,v,x,y;
}e[40000];
int etop,d[1050];
int T,n,m;
void add(int u,int v,int x,int y){
e[etop].v=v;e[etop].x=x;
e[etop].y=y;e[etop].nxt=d[u];
d[u]=etop++;
}
bool vis[1050];
bool f;
void dfs(int u){
for(int k=d[u];k!=-1;k=e[k].nxt){
if(vis[e[k].v]){
if(!(s[u]/e[k].x==s[e[k].v]/e[k].y)){
f=0;return;
}
}
else{
vis[e[k].v]=1;
s[e[k].v]=(s[u]/e[k].x)*e[k].y;
dfs(e[k].v);
if(f==0) return;
}
}
}
int main(){
scanf("%d",&T);
for(int t=1;t<=T;t++){
printf("Case #%d: ",t);
memset(s,0,sizeof(s));
memset(vis,0,sizeof(vis));
memset(d,-1,sizeof(d));
etop=0;
scanf("%d%d",&n,&m);
for(int i=1,u,v,x,y;i<=m;i++){
scanf("%d%d%d%d",&u,&v,&x,&y);
add(u,v,x,y);
add(v,u,y,x);
}
f=1;
for(int i=1;i<=n;i++)if(!vis[i]){
s[i].x=s[i].y=1;
dfs(i);
if(!f)break;
}
if(f) printf("Yes\n");
else printf("No\n");
}
return 0;
}