zoukankan      html  css  js  c++  java
  • Intel Code Challenge Final Round (Div. 1 + Div. 2, Combined) C.Ray Tracing (模拟或扩展欧几里得)

    http://codeforces.com/contest/724/problem/C

     

    题目大意:

    在一个n*m的盒子里,从(0,0)射出一条每秒位移为(1,1)的射线,遵从反射定律,给出k个点,求射线分别第一次经过这些点的时间。

     

    解法一: (模拟)

    射线不管怎么反射,都是和水平方向成45°角的,也就是说每一段射线上的点,横坐标和纵坐标的和或者差相等。 把每一个点放入它所对应的对角线里,然后模拟射线的路径就好。

    代码:

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <map>
    #include <cstdlib>
    #include <set>
    #include <queue>
    using namespace std;
    
    #define X first
    #define Y second
    #define Mod 1000000007
    #define N 100110
    #define M 200110
    typedef long long ll;
    typedef pair<int,int> pii;
    
    const ll INF=1e18;
    int n,m,k;
    ll ans[N];
    pii p[N];
    vector<int> dig1[N<<1],dig2[N<<1];
    
    int main()
    {
        //freopen("in.in","r",stdin);
        //freopen("out.out","w",stdout);
    
        scanf("%d%d%d",&n,&m,&k);
        for (int i=0;i<k;i++) 
        {
            scanf("%d%d",&p[i].X,&p[i].Y);
            dig1[p[i].X-p[i].Y+N].push_back(i);
            dig2[p[i].X+p[i].Y].push_back(i);
            ans[i]=INF;
        }
        
        int x=0,y=0,d,tx,lx,op=1; ll t=0;
        do
        {
            lx=x;
            if (x==0)
            {
                if (op) d=min(n,m-y),x+=d,y+=d;
                else d=min(n,y),x+=d,y-=d;
            }
            else if (x==n)
            {
                if (op) d=min(n,y),x-=d,y-=d;
                else d=min(n,m-y),x-=d,y+=d;                
            }
            else if (y==0)
            {
                if (op) d=min(m,n-x),x+=d,y+=d;
                else d=min(m,x),x-=d,y+=d;
            }
            else
            {
                if (op) d=min(m,x),x-=d,y-=d;
                else d=min(m,n-x),x+=d,y-=d;    
            }
            if (op)
            {
                for (int i=0;i<dig1[x-y+N].size();i++)
                {
                    tx=p[dig1[x-y+N][i]].X;
                    ans[dig1[x-y+N][i]]=min(ans[dig1[x-y+N][i]],t+abs(tx-lx));
                }
            }
            else
            {
                for (int i=0;i<dig2[x+y].size();i++)
                {
                    tx=p[dig2[x+y][i]].X;
                    ans[dig2[x+y][i]]=min(ans[dig2[x+y][i]],t+abs(tx-lx));
                }
            }
            t+=d,op^=1;
            //cout<<x<<" "<<y<<endl;
        }while (!((x==0 && y==0) || (x==0 && y==m) || (x==n && y==0) || (x==n && y==m)));
        
        for (int i=0;i<k;i++) printf("%I64d
    ",ans[i]==INF? -1:ans[i]);
        return 0;
    }
    View Code

    解法二:(扩展欧几里得)

    水平方向和竖直方向位移可以先分开考虑。

    对于水平方向,每秒+1,然后n秒后,碰到墙壁,反弹,然后每秒-1,n秒后再次碰壁,又变成每秒+1.依次类推以2n为周期。

    假设横坐标要从0变成x,那么只可能两种情况:

    $t equiv x (mod 2n)$

    $t equiv 2n-x (mod 2n)$

    同理纵坐标要从0变成y,也只有两种情况:

    $t equiv y (mod 2m)$

    $t equiv 2m-y (mod 2m)$

     

    同时考虑横纵坐标,一共四种情况,每次相当于解一个线性同余方程组。

    解线性同余方程组可以参考这篇: http://www.cnblogs.com/vb4896/p/4009181.html

    代码:

     

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <map>
    #include <cstdlib>
    #include <set>
    #include <queue>
    using namespace std;
    
    #define X first
    #define Y second
    #define Mod 1000000007
    #define N 200110
    #define M 200110
    
    typedef long long ll;
    typedef pair<int,int> pii;
    
    const ll INF=4e18;
    
    void ex_gcd(ll a,ll b,ll &x,ll &y,ll &d)
    {
        if (b==0){x=1,y=0,d=a;}
        else
        {
            ex_gcd(b,a%b,y,x,d);
            y-=a/b*x;
        }
    }
    
    ll Solve(ll a1,ll a2,ll m1,ll m2)
    {
        ll x,y,d,lcm;
        ex_gcd(m1,m2,x,y,d);
        if ((a2-a1)%d!=0) return INF;
    
        lcm=m1/d*m2;
        x*=(a2-a1)/d;
        x=x*m1+a1;
        x=(x%lcm)+lcm;
        if (x>=lcm) x-=lcm;
        
        return x;
    }
    
    
    int main()
    {
        //freopen("in.in","r",stdin);
        //freopen("out.out","w",stdout);
    
        int n,m,k;
        scanf("%d%d%d",&n,&m,&k);
        for (int i=1;i<=k;i++)
        {
            ll ans=INF; int x,y;
            scanf("%d%d",&x,&y);
            ans=min(ans,Solve(x,y,2*n,2*m));
            ans=min(ans,Solve(2*n-x,y,2*n,2*m));
            ans=min(ans,Solve(2*n-x,2*m-y,2*n,2*m));
            ans=min(ans,Solve(x,2*m-y,2*n,2*m));
            if (ans==INF) ans=-1;
            printf("%I64d
    ",ans);
        }
    
        return 0;
    }
    View Code

     

    再来一发福利: hdu 5114   和这题的方法类似,可以顺手切了

    http://acm.hdu.edu.cn/showproblem.php?pid=5114

    题目大意:

    n*m的盒子里给出2个球的坐标,2个球一开始速度都是(1,1),遵守反射定律,求相遇点的坐标。

     

    思路:

    1.首先相遇点的坐标要么是整数,要么是n+0.5的形式,所以可以实现把所有坐标乘以2,可以避免小数,最后除以2就好。

    2.同样横纵坐标分开考虑。  

    不难求出横纵坐标第一次相同的时间分别为$frac{2n-x_1-x_2}{2}$   $frac{2m-y_1-y_2}{2}$

    然后水平方向相遇的周期为n,竖直方向周期为m。所以

    $t equiv frac{2n-x_1-x_2}{2} (mod n)$

    $t equiv frac{2m-y_1-y_2}{2} (mod m)$

    解出t,然后模拟一下就可以求出相遇点的坐标了。

    代码:

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    
    #define N 1010
    typedef long long ll;
    
    int T,n,m,x1,x2,y1,y2;
    
    void ex_gcd(ll a,ll b,ll &x,ll &y,ll &d)
    {
        if (b==0) {x=1,y=0,d=a;}
        else
        {
            ex_gcd(b,a%b,y,x,d);
            y-=a/b*x;
        }
    }
    
    ll Solve()
    {
        if (x1==x2 && y1==y2) return 0;
        if (x1==x2) return (2*m-y1-y2)/2;
        if (y1==y2) return (2*n-x1-x2)/2;
        
        ll t1=(2*n-x1-x2)/2,t2=(2*m-y1-y2)/2,x,y,d;
        ex_gcd(n,m,x,y,d);
        if ((t2-t1)%d!=0) return -1;
        x*=(t2-t1)/d;
        
        ll lcm=n/d*m;
        ll ans=(x*n+t1)%lcm;
        if (ans<0) ans+=lcm;
        return ans;
    }
    
    int main()
    {
        //freopen("in.in","r",stdin);
        //freopen("out.out","w",stdout);
        
        scanf("%d",&T);
        for (int cas=1;cas<=T;cas++)
        {
            printf("Case #%d:
    ",cas);
            scanf("%d%d",&n,&m); n<<=1,m<<=1;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            x1<<=1,y1<<=1,x2<<=1,y2<<=1;
            ll t=Solve(); 
            if (t==-1) {printf("Collision will not happen.
    "); continue;}
            
            int tx=t%(2*n),ty=t%(2*m),x=x1,y=y1;
            if (x+tx<=n) tx=x+tx;
            else if (2*n-x-tx>=0) tx=2*n-x-tx;
            else tx-=n+n-x;
            
            if (y+ty<=m) ty=y+ty;
            else if (2*m-y-ty>=0) ty=2*m-y-ty;
            else ty-=m+m-y;    
            
            printf("%.1lf %.1lf
    ",tx/2.0,ty/2.0);
        }
        
        return 0;
    }
    View Code

     

  • 相关阅读:
    poj 2312 Battle City
    poj 2002 Squares
    poj 3641 Pseudoprime numbers
    poj 3580 SuperMemo
    poj 3281 Dining
    poj 3259 Wormholes
    poj 3080 Blue Jeans
    poj 3070 Fibonacci
    poj 2887 Big String
    poj 2631 Roads in the North
  • 原文地址:https://www.cnblogs.com/vb4896/p/6090451.html
Copyright © 2011-2022 走看看