• 光:模拟,桶


    谁说我做不出来大模拟题的?

    大码量警告:我4.2k,skyh 3.6k,tdcp 4.9k.orz mikufun 2.7k

    其实绝大多数部分都是在复制粘贴。

    首先,60分暴力不讲可以的嘛?纯粹模拟诶。

     1 #include<cstdio>
     2 int n,m,x,nx,ny,ndir,ans,bl[1005][1005],al[1005][1005][4],ne[200005],sw[200005];//0NE 1SE 2SW 3Nw
     3 const int tox[4]={1,1,-1,-1},toy[4]={-1,1,1,-1},c1[4]={3,2,1,0},c2[4]={1,0,3,2};
     4 char s[3];
     5 int main(){
     6     scanf("%d%d%d",&n,&m,&x);
     7     for(int i=1,a,b;i<=x;++i)scanf("%d%d",&a,&b),bl[a][b]=1;
     8     for(int i=0;i<=n+1;++i)bl[i][0]=bl[i][m+1]=1;
     9     for(int i=0;i<=m+1;++i)bl[n+1][i]=bl[0][i]=1;
    10     scanf("%d%d%s",&nx,&ny,s);
    11     if(s[0]=='S'&&s[1]=='E')ndir=1;
    12     if(s[0]=='S'&&s[1]=='W')ndir=2;
    13     if(s[0]=='N'&&s[1]=='E')ndir=0;
    14     if(s[0]=='N'&&s[1]=='W')ndir=3;
    15     while(1){//printf("%d %d
    ",nx,ny);
    16         if(al[nx][ny][ndir])break;
    17         al[nx][ny][ndir]=1;
    18         if(!bl[nx+tox[ndir]][ny+toy[ndir]]){nx+=tox[ndir];ny+=toy[ndir];continue;}
    19         if(bl[nx+tox[ndir]][ny]&&bl[nx][ny+toy[ndir]]){ndir=(ndir>=2?ndir-2:ndir+2);continue;}
    20         if(bl[nx+tox[ndir]][ny]){ny+=toy[ndir];ndir=c1[ndir];continue;}
    21         if(bl[nx][ny+toy[ndir]]){nx+=tox[ndir];ndir=c2[ndir];continue;}
    22         ndir=(ndir>=2?ndir-2:ndir+2);
    23     }
    24     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)
    25         if(al[i][j][0]||al[i][j][1]||al[i][j][2]||al[i][j][3]) ans++;
    26     printf("%d",ans);
    27 }
    只有1.1k

    多开几个大小为4的数组(5也行,有的人从1开始记的)

    存一大堆东西:走之后的坐标变化,180度反弹后的方向,遇到竖着的墙后折射方向,以及横着的。。。

    1 //0NE 1SE 2SW 3Nw
    2 const int tox[4]={1,1,-1,-1},//横坐标变化
    3     toy[4]={-1,1,1,-1},//纵坐标
    4     c1[4]={3,2,1,0},//撞横着的墙
    5     c2[4]={1,0,3,2},//竖着的
    6     c3[4]={2,3,0,1};//180度

    不要问我是怎么算出来的,一个一个在演算纸上画呗。

    然后你就可以把4个大if合成为1个while了。

    说白了,就是要搞明白东南西北。

    而且,懒得判是否碰到边框的懒人们(包括我),只要把最外边一圈设置为障碍统一处理就好了。

    说满分算法吧。

    其实满分算法我并没有按照上面这么做,因为它要表示的东西太多,容易混。

    我选择了switchcase语句块,用来处理不同的方向。当然也可以ifelse。

    如果你不知道switchcase语句,请百度搜索“C++语言基础”。

    说实在的还是模拟。

    我们可以发现,障碍的总数并不是很多,它反弹的次数也并不会很多。

    每两次反弹之间,走过的路径是一条直线,一下一下的枚举显然过于浪费。

    我们考虑有什么方法能直接跳过这一部分的枚举过程。

    为了更符合人的直觉,我们重新定义一下坐标,以左下角为(1,1)右上角为(n,m)

    这样的话就是向右,上是正方向了。

    对于每一次的光路它只有两种可能:左下<->右上 左上<->右下

    对于第一种,随着x++,那y++。

    对于第二种,随着x++,那y--。

    做过天天爱跑步吗?第二次出现相关题目了。这次更像了。

    在那题里,出发链dep++,time--,终点链dep++,time++。

    当时我们的思路是前者dep+time是定值,dep-time是定值。

    那么这题的定值也很显然了,一个x+y,一个x-y

    先开两个vector,分别存这两个值,x-y可能负了,改为插入x-y+100000就行

    对于每一个障碍,我们都把它们放进去,记得包括周围的你加上的那一圈。

    接下来需要几个小结论。

    1,每一个格子只会被 左下<->右上 左上<->右下 两种光线的其中一种经过。

    证明:对于光线所处的每一个状态,若位置为(x,y),设di表示方向,方向为左下<->右上时为1,否则为0。

    那么对于同一个光线,其路径上的所有点x+y+di奇偶性不变。

    如果它没碰到障碍,di不变,x和y同时变化1,和的奇偶性不变。

    如果它遇到障碍转弯180度,di,x,y均不变,和也不变。

    如果它旋转了90度,那么按照反射规则,它像错了一位一样,x,y其中一个变化了1,di变化了1,和的奇偶性不变。

    所以,对于每一个光线,只要我们能确定它的某一时刻的位置与方向:

    那么我们就能确定它在其它所有位置上的方向(除非这个位置没有被经过)

    2,光线的路径一定是一个完整的环,而不是环+链。也就是说,它一定还会不止一次的以起始状态经过起始点。

    再换一句话说,最后的循环节中一定含有起始点。

    反证法:如果它是从起点进入,最后进入了一个闭合的不含起点的环。

    那么我们随意选取环中的一个点,按照原来的方向的反方向射回去。

    因为光路是可逆的(初中物理,本题适用,这个我真的懒得证了)

    所以它一定能射到起点。

    而因为它是在一个环里的点,它会按照原路径反向走,只能在环里绕啊绕的。

    环反向之后还是那个环而不是链。

    那怎么能经过环外的起点呢?所以假设不成立。

    这个结论的应用是什么呢?假如光从x1,y1出发,遇到的下一块障碍为x2,y2

    而这条光路上的上一个障碍是x0,y0,那么我们可以把起点重设为x0,y0

    这样的话就避免了开始会跑半条光路的尴尬情况。

    好多人都是直接用的这两条结论,我也是,仔细一想,不证明的话就是在纯粹的搬运题解啊。

    那么接下来,有了这两条结论,问题就变得简单了:

    找起点状态所在的环,求环长。

    具体的代码实现还是要讲一讲的:

    我们现在已经把所有的障碍放进vector里面了,再把它们按照x的大小sort。

    那么里面的障碍就已经按照左下->右上 左上->右下的顺序排好了。

    对于我们的一个已知状态,我们在对应的vector里面找到下一个x更大/小的障碍,直接从原状态跳到那里就好!

    怎么找?vector自带lower_bound多好呀!

    然而我不知道为什么手打了4个二分...

    upper1,lower1,upper2,lower2...

    一个使用来找x前缀,一个是后缀。两个vector的分开打。。。(打splay吧!

    还打了一个find函数,用来找vector里某个位置是否存在障碍。

    不用啊。。系统自带的就行。虽说我不会玩指针。

    因为障碍是没有重复的,所以前缀和后缀之间只差了一位。加减一下就好了。

    拿代码讲吧。

     1 switch(ndir){
     2             case 0:{//以NE方向为例,注意这里的坐标和题目里的含义一样,而不是右上为正方向
     3                 res=lx+ly; nxt=upper1(res,lx);//lx,ly为目前位置,nxt为x下一个更大的障碍在vector里的下标
     4                 nx=v1[res][nxt]; ny=res-nx;//nx,ny为要碰到的下一个障碍的坐标
     5                 if(v1al[res][nxt-1]&1){ok=1;break;}//如果已经用相同的状态走过这条光路,退出
     6                 //ok是while循环的退出标记,因为在这里break只会把switch给break掉
     7                 v1al[res][nxt-1]^=1; //标记,这段光路已经被走过
     8                 if(!(v1al[res][nxt-1]&2))ans+=nx-lx;//如果这条光路的反向光路没有被走过,总距离累加
     9                 //这里用了1表示正向边,2表示反向边,重复经过只会累加一次答案
    10                 if(find(nx-1,ny)&&find(nx,ny+1)) ndir=2,lx=nx-1,ly=ny+1;
    11                 //如果遇到了“墙角”,180度反射,下一个出发位置在目前障碍的左下方(SW方向)
    12                 else if(find(nx-1,ny)) ndir=1,lx=nx,ly=ny+1;
    13                 //如果遇到了横着的墙,方向改为SE,障碍正下方的格子作为出发点
    14                 else if(find(nx,ny+1)) ndir=3,lx=nx-1,ly=ny;//竖着的同理
    15                 else ndir=2,lx=nx-1,ly=ny+1;//如果只有那一块,180度反射,同“墙角”
    16                 break;//switch语句必需。否则会立刻执行下一个case
    17             }

    其余方向同理。有些东西稍微变化一下就好。

     1 case 2:{//SW
     2                 res=lx+ly; nxt=lower1(res,lx);
     3                 nx=v1[res][nxt]; ny=res-nx;
     4                 if(v1al[res][nxt]&2){ok=1;break;}
     5                 v1al[res][nxt]^=2; 
     6                 if(!(v1al[res][nxt]&1))ans+=lx-nx;
     7                 if(find(nx+1,ny)&&find(nx,ny-1)) ndir=0,lx=nx+1,ly=ny-1;
     8                 else if(find(nx+1,ny)) ndir=3,lx=nx,ly=ny-1;
     9                 else if(find(nx,ny-1)) ndir=1,lx=nx+1,ly=ny;
    10                 else ndir=0,lx=nx+1,ly=ny-1;
    11                 break;
    12             }
    给出与之对应的SW方向的代码,注意有哪些地方改变了

    自己想想哪些地方不一样吧。什么折射方向之类的。

    再补充一下结论2的应用。

    1 case 0:{
    2     res=lx+ly; nxt=upper1(res,lx)-1;
    3     lx=v1[res][nxt]+1; ly=res-lx;
    4     break;
    5 }

    其实和上面那个SW方向的代码挺像,毕竟就是往SW方向捯了一个障碍。注意不要累加答案。

    另一个同理的想法是把起点直接重设为下一个障碍的位置,那么代码会和正常光路的NE很像。

    毕竟是个环,从哪里开始都一样。我没这么打,就不给出代码了。

    这俩玩意复制粘贴四遍再改改就好了。

    码量极大注意细节。

      1 #include<cstdio>
      2 #include<vector>
      3 #include<algorithm>
      4 using namespace std;
      5 #define addx 100003
      6 long long ans;int n,m,k,lx,ly,nx,ny,nxt,res,ok,ndir;//0NE 1SE 2SW 3NW
      7 char s[3];
      8 vector<int>v1[200005],v2[200005];//v1:x+y v2:x-y+10w
      9 vector<int>v1al[200005],v2al[200005];
     10 int lower1(int x,int w){
     11     int l=0,r=v1[x].size()-1;
     12     while(l<r-1)if(v1[x][l+r>>1]>w)r=(l+r>>1)-1;else l=l+r>>1;
     13     if(v1[x][r]<w)return r;return l;
     14 }
     15 int upper1(int x,int w){
     16     int l=0,r=v1[x].size()-1;
     17     while(l<r-1)if(v1[x][l+r>>1]>w)r=l+r>>1;else l=(l+r>>1)+1;
     18     if(v1[x][l]>w)return l;return r;
     19 }
     20 int lower2(int x,int w){
     21     int l=0,r=v2[x].size()-1;
     22     while(l<r-1)if(v2[x][l+r>>1]>w)r=(l+r>>1)-1;else l=l+r>>1;
     23     if(v2[x][r]<w)return r;return l;
     24 }
     25 int upper2(int x,int w){
     26     int l=0,r=v2[x].size()-1;
     27     while(l<r-1)if(v2[x][l+r>>1]>w)r=l+r>>1;else l=(l+r>>1)+1;
     28     if(v2[x][l]>w)return l;return r;
     29 }
     30 bool find(int x,int y){
     31     int l=0,r=v1[x+y].size()-1;
     32     while(l<r-1)if(v1[x+y][l+r>>1]>x)r=(l+r>>1)-1;else l=l+r>>1;
     33     if(v1[x+y][r]==x||v1[x+y][l]==x)return true;return false;
     34 }
     35 int main(){
     36     scanf("%d%d%d",&n,&m,&k);
     37     for(int i=1,a,b;i<=k;++i)
     38         scanf("%d%d",&a,&b),
     39         v1[a+b].push_back(a), v2[a-b+addx].push_back(a),
     40         v1al[a+b].push_back(0), v2al[a-b+addx].push_back(0);
     41     for(int i=0;i<=n+1;++i)
     42         v1[i].push_back(i),v2[i+addx].push_back(i),
     43         v1[i+m+1].push_back(i),v2[i-1-m+addx].push_back(i),
     44         v1al[i].push_back(0),v2al[i+addx].push_back(0),
     45         v1al[i+m+1].push_back(0),v2al[i-1-m+addx].push_back(0);
     46     for(int i=1;i<=m;++i)
     47         v1[i].push_back(0),v2[addx-i].push_back(0),
     48         v1[n+1+i].push_back(n+1),v2[n+1-i+addx].push_back(n+1),
     49         v1al[i].push_back(0),v2al[addx-i].push_back(0),
     50         v1al[n+1+i].push_back(0),v2al[n+1-i+addx].push_back(0);
     51     for(int i=1;i<=200004;++i)sort(v1[i].begin(),v1[i].end());
     52     for(int i=1;i<=200004;++i)sort(v2[i].begin(),v2[i].end());
     53     scanf("%d%d%s",&lx,&ly,s);
     54     if(s[0]=='S'&&s[1]=='E')ndir=1;
     55     if(s[0]=='S'&&s[1]=='W')ndir=2;
     56     if(s[0]=='N'&&s[1]=='E')ndir=0;
     57     if(s[0]=='N'&&s[1]=='W')ndir=3;
     58     switch(ndir){
     59         case 0:{
     60             res=lx+ly; nxt=upper1(res,lx)-1;
     61             lx=v1[res][nxt]+1; ly=res-lx;
     62             break;
     63         }
     64         case 1:{
     65             res=lx-ly+addx; nxt=upper2(res,lx)-1;
     66             lx=v2[res][nxt]+1; ly=lx+addx-res;
     67             break;
     68         }
     69         case 2:{
     70             res=lx+ly; nxt=lower1(res,lx)+1;
     71             lx=v1[res][nxt]-1; ly=res-lx;
     72             break;
     73         }
     74         case 3:{
     75             res=lx-ly+addx; nxt=lower2(res,lx)+1;
     76             lx=v2[res][nxt]-1; ly=lx+addx-res;
     77             break;
     78         }
     79     }
     80     while(1){
     81         switch(ndir){
     82             case 0:{//NE
     83                 res=lx+ly; nxt=upper1(res,lx);
     84                 nx=v1[res][nxt]; ny=res-nx;
     85                 if(v1al[res][nxt-1]&1){ok=1;break;}
     86                 v1al[res][nxt-1]^=1; 
     87                 if(!(v1al[res][nxt-1]&2))ans+=nx-lx;
     88                 if(find(nx-1,ny)&&find(nx,ny+1)) ndir=2,lx=nx-1,ly=ny+1;
     89                 else if(find(nx-1,ny)) ndir=1,lx=nx,ly=ny+1;
     90                 else if(find(nx,ny+1)) ndir=3,lx=nx-1,ly=ny;
     91                 else ndir=2,lx=nx-1,ly=ny+1;
     92                 break;
     93             }
     94             case 1:{//SE
     95                 res=lx-ly+addx; nxt=upper2(res,lx);
     96                 nx=v2[res][nxt]; ny=nx+addx-res;
     97                 if(v2al[res][nxt-1]&1){ok=1;break;}
     98                 v2al[res][nxt-1]^=1; 
     99                 if(!(v2al[res][nxt-1]&2))ans+=nx-lx;
    100                 if(find(nx-1,ny)&&find(nx,ny-1)) ndir=3,lx=nx-1,ly=ny-1;
    101                 else if(find(nx-1,ny)) ndir=0,lx=nx,ly=ny-1;
    102                 else if(find(nx,ny-1)) ndir=2,lx=nx-1,ly=ny;
    103                 else ndir=3,lx=nx-1,ly=ny-1;
    104                 break;
    105             }
    106             case 2:{//SW
    107                 res=lx+ly; nxt=lower1(res,lx);
    108                 nx=v1[res][nxt]; ny=res-nx;
    109                 if(v1al[res][nxt]&2){ok=1;break;}
    110                 v1al[res][nxt]^=2; 
    111                 if(!(v1al[res][nxt]&1))ans+=lx-nx;
    112                 if(find(nx+1,ny)&&find(nx,ny-1)) ndir=0,lx=nx+1,ly=ny-1;
    113                 else if(find(nx+1,ny)) ndir=3,lx=nx,ly=ny-1;
    114                 else if(find(nx,ny-1)) ndir=1,lx=nx+1,ly=ny;
    115                 else ndir=0,lx=nx+1,ly=ny-1;
    116                 break;
    117             }
    118             case 3:{//NW
    119                 res=lx-ly+addx; nxt=lower2(res,lx);
    120                 nx=v2[res][nxt]; ny=nx+addx-res;
    121                 if(v2al[res][nxt]&2){ok=1;break;}
    122                 v2al[res][nxt]^=2; 
    123                 if(!(v2al[res][nxt]&1))ans+=lx-nx;
    124                 if(find(nx+1,ny)&&find(nx,ny+1)) ndir=1,lx=nx+1,ly=ny+1;
    125                 else if(find(nx+1,ny)) ndir=2,lx=nx,ly=ny+1;
    126                 else if(find(nx,ny+1)) ndir=0,lx=nx+1,ly=ny;
    127                 else ndir=1,lx=nx+1,ly=ny+1;
    128                 break;
    129             }
    130         }
    131         if(ok)break;
    132     }
    133     printf("%lld
    ",ans);
    134 }
    相信我一次,码风还好(134行)
  • 相关阅读:
    自相关函数 互相关函数 平稳随机过程
    MC323发短信程序+MSP430F149
    labview notes
    DSSS FHSS
    dBm与功率(w)换算关系!
    int 正负相乘
    51单片机 1602液晶显示
    51单片机 AD转换
    51单片机 蜂鸣器
    51单片机 串口通信
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11228487.html
Copyright © 2020-2023  润新知