光:模擬,桶


誰說我做不出來大模擬題的?

大碼量警告:我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\n",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了。

說白了,就是要搞明白東南西北。

而且,懶得判是否碰到邊框的懶人們(包括我),只要把最外邊一圈設置為障礙統一處理就好了。

說滿分算法吧。

其實滿分算法我並沒有按照上面這么做,因為它要表示的東西太多,容易混。

我選擇了switch\case語句塊,用來處理不同的方向。當然也可以if\else。

如果你不知道switch\case語句,請百度搜索“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\n",ans);
134 }
相信我一次,碼風還好(134行)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM