{题解}[jzoj4823] 【NOIP2016提高A组集训第1场10.29】小W学物理

传送门

Description

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

Analysis

很显然,不会出现环
如果用镜子摆成一个环,那你该如何进入呢?!
准确的说,还是会出现环的。
因为可以一开始就在环里……
也就是说,唯一的环一定会经过原点!
特判即可。
也因为没有环,
所以同一面镜子不会重复经过。
最坏情况 O(2n)
注意细节。

Tips:从一面镜子走向另一面镜子时,
只需要排序,就是序列中的下一面镜子

千万不要作死二分!!!!

Code

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const long long N = 101000, M = 1001000000, T = 1000000000000000000;
const long long 
    ctbx[4]={-1,1,0,0},
    ctby[4]={0,0,1,-1},
    zx[3][4]={{0,0,0,0},{3,2,1,0},{2,3,0,1}};
struct node
{
    long long x,y,num;
}a[N];
bool cmp(node x,node y){return (x.x<y.x)||((x.x==y.x)&&(x.y<y.y));}
bool cmp2(node x,node y){return (x.y<y.y)||((x.y==y.y)&&(x.x<y.x));}
long long n,m,t;
long long s,Ansx,Ansy;
long long w[N][4],num[N][4];
long long bz[N][4],c[N][3];
int main()
{
    freopen("mir.in","r",stdin);freopen("mir.out","w",stdout);
    long long f = 1;
    scanf("%lld%lld%lld", &n, &m, &t);
    for (int i = 1;i <= n;i ++)
    {
        char ch;
        scanf("%lld %lld %c\n", &a[i].x, &a[i].y, &ch);
        c[i][2] = (ch=='/')?1:2;
        a[i].num = i;
        c[i][0] = a[i].x, c[i][1] = a[i].y;
    }
    //a[i] x,y坐标 d序号(方便其他数组) 
    sort(a + 1, a + n + 1, cmp);
    for (int i = 1;i <= n;i ++)
    {
        if (a[i - 1].x == a[i].x) 
            w[a[i].num][3] = a[i].y - a[i - 1].y,
            num[a[i].num][3] = a[i - 1].num;
        //a[i-1].y较小 
        if (a[i + 1].x == a[i].x) 
            w[a[i].num][2] = a[i + 1].y - a[i].y,
            num[a[i].num][2] = a[i + 1].num;
        //a[i+1].y较大 
        //num[i] 原序号为i的镜子 ([0]上[1]下[2]右[3]左)第一个镜子序号 
        //w[i] 原序号为i的镜子 ([0]上[1]下[2]右[3]左)第一个镜子距离 
    }
    sort(a + 1,a + n + 1,cmp2);
    for (int i = 1;i <= n;i ++)
    {
        if (a[i - 1].y == a[i].y) 
            w[a[i].num][0] = a[i].x - a[i - 1].x,
            num[a[i].num][0] = a[i - 1].num;
        if (a[i + 1].y == a[i].y) 
            w[a[i].num][1] = a[i + 1].x - a[i].x,
            num[a[i].num][1] = a[i + 1].num;
        //优化:先走几步... 
        if (a[i].y == 0 && a[i].x > 0 && s == 0)
            if (a[i].y < t) 
                s = a[i].num,
                t -= a[i].x;
            else 
                Ansx = t,
                Ansy = 0,
                t = 0;
    }
    if (s == 0) 
        Ansx = t,
        Ansy = 0,
        t = 0;
    for(;t;)
    {
        if(bz[s][f] != 0) 
            t = t % (bz[s][f] - t);
        //bz判环 
        bz[s][f] = t; 
        f = zx[c[s][2]][f];
        //zx 转向专用数组! 
        //f 方向 
        if (w[s][f] < t && num[s][f] != 0) 
            t -= w[s][f],
            s = num[s][f];
            //考虑走几步... 
        else 
            Ansx = c[s][0] + ctbx[f] * t,
            Ansy = c[s][1] + ctby[f] * t,
            t = 0;
    }
    printf("%lld %lld", Ansx, Ansy);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值