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

37 篇文章 0 订阅

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

Solution

乍一看好熟悉
曾经有一题几乎一样的,模拟,但是这题的范围!!
显然时间复杂度不能和T有关
一种显然的思路,找出每个点上下左右的最近的点,每次跳过去就行了,这样每个点最多经过4次,时间复杂度为 O(4n)
但是有一点,因为它的出发点在图内,所以是可能有循环的
那就对于每个点的每个方向做一个标记,记录到达的时间,那么第二次到达时,就可以跳过循环节,直接进入最后一次循环

Code

有些人表示打了100多行,我很奇怪,怎么打的?我的代码量折半

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 200100
#define ll long long
using namespace std;
struct node{
    ll x,y,z,d;
};
node a[N];
ll l[N][4],b[N][4],s,ax,ay,c[N][3],bz[N][4],ddd[4]={-1,1,0,0},dddd[4]={0,0,1,-1},ct[3][4]={{0,0,0,0},{3,2,1,0},{2,3,0,1}};
bool cnt(node x,node y){return (x.x<y.x)||((x.x==y.x)&&(x.y<y.y));}
bool cnt2(node x,node y){return (x.y<y.y)||((x.y==y.y)&&(x.x<y.x));}
int main()
{
    freopen("mir.in","r",stdin);freopen("mir.out","w",stdout);
    ll t,n,m,f=1;
    scanf("%lld%lld%lld",&n,&m,&t);
    fo(i,1,n) 
    {
        char ch;
        scanf("%lld %lld %ch\n",&a[i].x,&a[i].y,&ch);
        if(ch=='/') c[i][2]=1;else c[i][2]=2;
        a[i].d=i;c[i][0]=a[i].x;c[i][1]=a[i].y;
    }
    sort(a+1,a+n+1,cnt);
    fo(i,1,n)
    {
        if(a[i-1].x==a[i].x) l[a[i].d][3]=a[i].y-a[i-1].y,b[a[i].d][3]=a[i-1].d;
        if(a[i+1].x==a[i].x) l[a[i].d][2]=a[i+1].y-a[i].y,b[a[i].d][2]=a[i+1].d;
    }
    sort(a+1,a+n+1,cnt2);
    fo(i,1,n)
    {
        if(a[i-1].y==a[i].y) l[a[i].d][0]=a[i].x-a[i-1].x,b[a[i].d][0]=a[i-1].d;
        if(a[i+1].y==a[i].y) l[a[i].d][1]=a[i+1].x-a[i].x,b[a[i].d][1]=a[i+1].d;
        if(a[i].y==0&&a[i].x>0&&s==0)
            if(a[i].y<t) s=a[i].d,t-=a[i].x;else ax=t,ay=0,t=0;
    }
    if(s==0) ax=t,ay=0,t=0;
    for(;t;)
    {
        if(bz[s][f]!=0) t=t%(bz[s][f]-t);
        bz[s][f]=t;f=ct[c[s][2]][f];
        if(l[s][f]<t&&b[s][f]!=0) t-=l[s][f],s=b[s][f];
        else ax=c[s][0]+ddd[f]*t,ay=c[s][1]+dddd[f]*t,t=0;
    }
    printf("%lld %lld",ax,ay);
    fclose(stdin);fclose(stdout);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值