zoukankan      html  css  js  c++  java
  • [Sdoi2016]齿轮

    4602: [Sdoi2016]齿轮

    Time Limit: 10 Sec  Memory Limit: 512 MB
    Submit: 613  Solved: 324
    [Submit][Status][Discuss]

    Description

    现有一个传动系统,包含了N个组合齿轮和M个链条。每一个链条连接了两个组合齿轮u和v,并提供了一个传动比x 
    : y。即如果只考虑这两个组合齿轮,编号为u的齿轮转动x圈,编号为v的齿轮会转动y圈。传动比为正表示若编号
    为u的齿轮顺时针转动,则编号为v的齿轮也顺时针转动。传动比为负表示若编号为u的齿轮顺时针转动,则编号为v
    的齿轮会逆时针转动。若不同链条的传动比不相容,则有些齿轮无法转动。我们希望知道,系统中的这N个组合齿
    轮能否同时转动。
     

     

    Input

    有多组数据,第一行给定整数T,表示总的数据组数,之后依次给出T组数据。每一组数据的第一行给定整数N和
    M,表示齿轮总数和链条总数。之后有M行,依次描述了每一个链条,其中每一行给定四个整数u,v,x和y,表示
    只考虑这一组联动关系的情况下,编号为u的齿轮转动x圈,编号为v的齿轮会转动y圈。请注意,x为正整数,而y为
    非零整数,但是y有可能为负数。
    T<=32,N<=1000,M<=10000且x与y的绝对值均不超过100
     

     

    Output

    输出T行,对应每一组数据。首先应该输出标识这是第几组数据,参见样例输出。之后输出判定结果,如果N个组合
    齿轮可以同时正常运行,则输出Yes,否则输出No。
     

     

    Sample Input

    2
    3 3
    1 2 3 5
    2 3 5 -7
    1 3 3 -7
    3 3
    1 2 3 5
    2 3 5 -7
    1 3 3 7

    Sample Output

    Case #1: Yes
    Case #2: No

    HINT

     

    Source

    【民间做法】:DFS
    //对于每组齿轮(u, v)连边,权值为y/x(反向边x/y)
    //dfs 判断矛盾 
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    typedef double real;
    inline void read(int &x){
        register char ch=getchar();int f=1;x=0;
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        x*=f;
    }
    const int N=1e5+5;
    const real eps=1e-6;
    struct edge{int v,next;real w;}e[N<<1];int tot,head[N];
    int T,n,m,cas;
    bool vis[N],flag;
    real val[N];
    inline void add(int x,int y,real z){
        e[++tot].v=y;e[tot].w=z;e[tot].next=head[x];head[x]=tot;
    }
    inline bool dfs(int x,real sp){
        vis[x]=1;
        val[x]=sp;
        for(int i=head[x];i;i=e[i].next){
            if(!vis[e[i].v]){
                if(dfs(e[i].v,sp*e[i].w))
                    return 1;
            }
            else{
                if(fabs(val[e[i].v]-sp*e[i].w)>eps)
                    return 1;
            }
        }
        return 0;
    }
    inline void clr(){
        tot=0;flag=0;
        memset(head,0,(n+1)<<2);
        memset(vis,0,(n+1)<<2);
    }
    int main(){
        for(read(T);T--;clr()){
            read(n);read(m);
            for(int i=1,u,v,x,y;i<=m;i++){
                read(u);read(v);
                read(x),read(y);
                add(u,v,(real)y/(real)x);
                add(v,u,(real)x/(real)y);
            } 
            for(int i=1;i<=n;i++) if(!vis[i]){
                if(dfs(i,1)){
                    flag=1;break;
                }
            }
            printf("Case #%d: %s
    ",++cas,flag?"No":"Yes");
        }
        return 0;
    }

     

    【官方做法】:加权并查集

    首先可以看出这个齿轮的转动关系是具有传递性的,如果知道x和y的关系,也知道y和z的关系,就可以推出x和z的关系。具有这种关系的东西一般都可以用加权并查集来搞。对于每个元素给它维护一个dis[x],表示的含义是如果x的代表元素转动1圈,x要转动几圈。每次读入一个限制如果x和y不在同一个集合里就进行合并。合并的时候先找到x的代表元素r1和y的代表元素r2,然后我们要把r2合并到r1上。要进行合并就要知道r1转一圈的时候r2转几圈。设从读入的信息可以知道x转一圈的时候y转w圈,那么如果r1转了一圈,y就转了dis[x]w圈;而当y转了dis[x]w圈的时候,r2转了dis[x]w/dis[y]圈。关系如下图:
    这里写图片描述 
    如果读入的x和y在一个集合里,那么就可以用维护的信息算出x转了一圈的时候y转了几圈,和读入的信息进行比对就可以判定是否发生矛盾。

    这个数据范围看起来好像很小的样子。。但是如果它出上n-1个信息,每次都是当齿轮i转动1圈的时候齿轮i+1转动100圈,那么最后就会发现当齿轮1转动一圈的时候齿轮n会转动100n圈。。这样的话就不能直接用int或者long long或者什么类似的东西来维护加权并查集的dis了。。用分解质因数似乎是比较稳的方法,但是听说用long double还是什么类似的东西也能过?

    #include<cmath>
    #include<cstdio>
    using namespace std;
    typedef double real;
    inline void read(int &x){
        register char ch=getchar();int f=1;x=0;
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        x*=f;
    }
    const int N=1e5+5;
    const real eps=1e-6;
    int T,n,m,cas;bool flag;
    int fa[N];real dis[N];
    int find(int x){
        int t;
        if(fa[x]!=x) t=find(fa[x]),dis[x]*=dis[fa[x]],fa[x]=t;
        return fa[x];
    }
    int main(){
        for(read(T);T--;flag=0){
            read(n);read(m);
            for(int i=1;i<=n;i++) fa[i]=i,dis[i]=1; 
            for(int i=1,r1,r2,u,v,x,y;i<=m;i++){
                read(u);read(v);read(x);read(y);
                r1=find(u);r2=find(v);
                if(r1==r2){
                    if(fabs(dis[v]/dis[u]-(real)y/(real)x)>eps){
                        flag=1;break;
                    }
                }
                else{
                    fa[r2]=r1;
                    dis[r2]=(dis[u]*y)/(dis[v]*x);
                }
            }
            printf("Case #%d: %s
    ",++cas,flag?"No":"Yes");
        }
        return 0;
    }

     

  • 相关阅读:
    asp.net 2.0 run
    Regular Expression
    assembly
    asp.net loading..
    session
    asp.net performance
    asp.net page order
    interface
    UVA 562 Dividing coins
    UVA 10003 Cutting Sticks
  • 原文地址:https://www.cnblogs.com/shenben/p/6852564.html
Copyright © 2011-2022 走看看