JZOJ4823. 【NOIP2016提高A组集训第1场10.29】小W学物理

Description

为了测试小W的物理水平,Mr.X在二维坐标系中放了N面镜子(镜子坐标绝对值不超过M),镜子均与坐标轴成45°角,所以一共有两种类型“/”和“\”。原点不会有镜子,任意一点最多只有一面镜子。
镜子两个面都能反光,而中间不透光,例如,对于一个“/”型镜子,下方向射入的光线会被反射到右方向,左方向射入的光线会被反射到上方向。
现在有一条光线从原点沿X轴正方向射出,求走过T路程后所在位置。

Input

第一行三个整数N,M,T。
第2到N+1行,每行两个整数Xi,Yi,表示镜子坐标,一个字符Si表示镜子类型

Output

一行两个整数,表示走过T路程后的坐标。

Sample Input

5 2 8
0 1 \
0 2 /
1 0 /
1 1 \
1 2 \

Sample Output

3 1

Data Constraint

对于10%的数据:N=1
对于25%的数据:T<=1000000
对于35%的数据:N<=1000
对于60%的数据:M<=1000
对于100%的数据:N<=100,000,M<=1,000,000,000,T<=10^18

分析

我们知道,如果不出现循环的情况,每一个镜子最多会经过两次,即每一个面经过一次。

因为光线沿直线传播,
所以光线从一个镜子出发,如果可以到达下一个镜子,那么下一个镜子一定是与它在同一行或者同一列,而且它们之间没有任何镜子。

只要我们知道光线从每一个镜子出发,向四个方向可以到达下一个镜子的位置,就可以模拟光线到达的镜子,从而得出答案。

如何预处理一个镜子可以到达的镜子?
我们可以对镜子的横纵坐标进行排序,
例如,第一关键字是横坐标,第二关键字是纵坐标,
那么,同一列的就会排在一起,
而在同一列里面也是有顺序的,
所以就可以快速求出一个镜子向上、向下到达的镜子。

同理,向左、向右也是一样的。

如果出现循环,则就是说光线一定再次经过原点,
这是只需要将T%一下光线走一圈的长度就可以了。

code(c++)

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string.h>
#include<cmath>
#include<math.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
struct note{long long x,y,z;};
int g[2][4]={{3,2,1,0},{1,0,3,2}};
int n,m,p[100003][5],i,j,fx;
long long t,sum,now;
note z[100003];
char ch;
bool cmp1(note x,note y)
{
    return (x.x<y.x)||((x.x==y.x)&&(x.y<y.y));
}
bool cmp2(note x,note y)
{
    return (x.y<y.y)||((x.y==y.y)&&(x.x<y.x));
}
bool cmp(note x,note y)
{
    return x.z<y.z;
}
int main()
{   
    freopen("mir.in","r",stdin);
    freopen("mir.out","w",stdout);
    scanf("%d%d%lld",&n,&m,&t);
    fo(i,1,n)
    {
        scanf("%lld%lld",&z[i].x,&z[i].y);
        ch=getchar();
        while((ch!='/')&&(ch!=92))ch=getchar();
        if(ch=='/')p[i][4]=1;
        z[i].z=i;
    }
    fx=1;
    z[0].x=z[0].y=-123456;
    sort(z+1,z+1+n,cmp2);
    fo(i,1,n)
    {
        if((z[i].y==0)&&(j==0)&&(z[i].x>0))
        {
            j=z[i].z;
            sum=z[i].x;
            fx=g[p[j][4]][fx];
        }
        if(z[i].y==z[i-1].y)
        {
            p[z[i].z][3]=z[i-1].z;
            p[z[i].z][1]=z[i+1].z;
        }else
        {
            p[z[i-1].z][1]=p[z[i].z][3]=0;
            p[z[i].z][1]=z[i+1].z;
        }
    }
    sort(z+1,z+1+n,cmp1);
    fo(i,1,n)
    {
        if(z[i].x==z[i-1].x)
        {
            p[z[i].z][2]=z[i-1].z;
            p[z[i].z][0]=z[i+1].z;
        }else
        {
            p[z[i-1].z][0]=p[z[i].z][2]=0;
            p[z[i].z][0]=z[i+1].z;
        }
    }
    if((j==0)||(t<=sum))
    {
        printf("%lld 0",t);
        return 0;
    }
    sort(z+1,z+1+n,cmp);
    t-=sum;
    while(t>0)
    {
        if(fx==1)now=z[p[j][1]].x-z[j].x;
        if(fx==2)now=z[j].y-z[p[j][2]].y;
        if(fx==3)now=z[j].x-z[p[j][3]].x;
        if(fx==0)now=z[p[j][0]].y-z[j].y;
        if((t<=now)||(p[j][fx]==0))
        {
            if(fx==1)printf("%lld %lld",z[j].x+t,z[j].y);
            if(fx==2)printf("%lld %lld",z[j].x,z[j].y-t);
            if(fx==3)printf("%lld %lld",z[j].x-t,z[j].y);
            if(fx==0)printf("%lld %lld",z[j].x,z[j].y+t);
            t=-1;
            break;
        }
        j=p[j][fx];
        fx=g[p[j][4]][fx];
        t-=now;
    }
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值