日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[***]HZOJ 跳房子

發布時間:2025/4/16 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [***]HZOJ 跳房子 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一道非常神仙的題.

算法一:對于20%的數據:?模擬,直接走K步,時間復雜度O(K)

算法二:對于40%的數據:走M*N步內必有一個循環節。直接走,找循環節,時間復雜度O(M*N)

正解大概有兩種做法(我是第三種……)

1.利用分塊思想,一行為一塊。用一個數組記錄第一列第i行走M步到達的位置jump[i]。在模擬過程中只要一行一行的走,不足一行再一步一步走,按行找循環節,時間復雜度O(M+N)。

更改操作:對于每個更改的單元格(x,y),我們回溯到第一列,找到第一列要更新的區間,更新jump[i] 。因為第一列到(x,y)的行是一個連續區間,在找的過程中,只需記錄區間上下邊界。復雜度為O(M+N)。

?這個的查詢比較容易,只是修改比較難搞,我研究了半天,證明了正確性和復雜度,又YY了一下代碼實現,覺得細節太多代碼又長想了想放棄了,目前xuefeng還在對拍中……這里只說一下修改:

結論1:如果第一列的點(i,1),(j,1)能到點(x,y),那么(i,1),(j,1)之間的點都會到(x,y),證明:路線不會交叉,自己YY一下就好啦。

于是設修改map[a][b]為e,那么對a,b左邊的三個點分別向后搜索找到最后到的點,然后向前搜索找現在能到這這個點的一段,將這一段的jump都改為向后搜索找到的點。顯然這三個點往前回溯找到的區間沒有交叉兩兩之間沒有間隙(因為路徑不會交叉,手模一下就好啦),而之前能到(a,b)的點一定能到前面的三個點,所以修改(a,b)所影響的點都會考慮到,這樣我們就證明了其正確性。然而在回溯時還有一個細節:如果對于每個點都向前分為三個點,那么時間復雜度無法保障,所以只能搜上下邊界保證搜索的軌跡是兩條線,但是這樣對嗎?大體是對的,不過有一個細節要考慮:

如圖,設我們當前在處理10這個點,那么我們應該遞歸處理9和7而不會去處理8,但是這是7卻不是最優的,顯然走8才能找到下邊界,所以要特判一種情況,當某個點無路可走時,跳的他上(下)面的點(有點難以理解,仔細想一下)。還有一個問題,這樣搜索的點又會多不少,時間復雜度能保障嗎?實際上是可以的,xuefeng開始覺得這樣最壞是n×m的,但是仔細想想會發現,搜索的線路是一條折線,而這條折線的角度只能是45,于是復雜度最壞n+m。

還有一個細節要判斷(我估計此時應該沒人想打這個算法了……),就是三個點往前搜最后都無路可走的情況。

等旁邊的xuefeng調完再放代碼吧,不過和我講的應該會有點(不少)出入……

(此處放代碼)

?

1 #include<bits/stdc++.h> 2 #define N 2005 3 #define LL long long 4 #define Inf 0x3f3f3f3f 5 using namespace std; 6 int n,m,q,len,where_x,to_time; 7 int a[N][N],jump[N],vis[N]; 8 char s[10]; 9 int read(){ 10 int x=0,f=1;char ch=getchar(); 11 while(!isdigit(ch))f=(ch=='-')?-1:1,ch=getchar(); 12 while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); 13 return x*f; 14 } 15 inline int _x(int x){ 16 return (x+n*2-1)%n+1; 17 } 18 inline int _y(int y){ 19 return (y+m*2-1)%m+1; 20 } 21 void get(int &x,int &y){// 22 y=_y(y+1);x=_x(x); 23 int imax=x,xu=_x(x-1),xd=_x(x+1); 24 if(a[imax][y]<a[xu][y])imax=xu; 25 if(a[imax][y]<a[xd][y])imax=xd; 26 x=imax; 27 } 28 void _get(int &x,int y){// 29 y=_y(y+1); 30 int imax=x,xu=_x(x-1),xd=_x(x+1); 31 if(a[_x(imax)][y]<a[xu][y])imax=x-1; 32 if(a[_x(imax)][y]<a[xd][y])imax=x+1; 33 x=imax; 34 } 35 36 void work(int l,int r){ 37 for(int i=l;i<=r;++i){ 38 int pos=i;int j=1; 39 do get(pos,j); 40 while(j!=1); 41 jump[i]=pos; 42 } 43 } 44 int L[N],R[N]; 45 void work_for(pair<int,int>x){ 46 L[x.second]=R[x.second]=x.first; 47 for(int i=x.second;i>=2;--i){ 48 if(L[i]>R[i])return;L[i-1]=Inf;R[i-1]=-Inf; 49 int e1=L[i]-1,e2=L[i],e3=L[i]+1; 50 _get(e1,i-1),_get(e2,i-1),_get(e3,i-1); 51 if(e1>=L[i]&&e1<=R[i])L[i-1]=L[i]-1; 52 else if(e2>=L[i]&&e2<=R[i])L[i-1]=L[i]; 53 else if(e3>=L[i]&&e3<=R[i])L[i-1]=L[i]+1; 54 55 e1=R[i]-1,e2=R[i],e3=R[i]+1; 56 _get(e1,i-1),_get(e2,i-1),_get(e3,i-1); 57 if(e3>=L[i]&&e3<=R[i])R[i-1]=R[i]+1; 58 else if(e2>=L[i]&&e2<=R[i])R[i-1]=R[i]; 59 else if(e1>=L[i]&&e1<=R[i])R[i-1]=R[i]-1; 60 } 61 pair<int,int>x1=x; 62 do get(x1.first,x1.second); 63 while(x1.second!=1); 64 for(int i=L[1];i<=R[1];++i)jump[_x(i)]=x1.first; 65 } 66 void Work(int x,int y){ 67 pair<int,int>e1,e2,e3; 68 e1.first=_x(x-1),e1.second=_y(y-1); 69 e2.first=x,e2.second=_y(y-1); 70 e3.first=_x(x+1),e3.second=_y(y-1); 71 work_for(e1);work_for(e2);work_for(e3); 72 } 73 void get_tarjan(int row){ 74 memset(vis,-1,sizeof vis); 75 for(int i=0;i<=n+2;++i){//花多少k到達 76 if(vis[row]!=-1){ 77 len=i-vis[row];//環長 78 where_x=row;//環開始的地方 79 to_time=vis[row];//開始時間 80 return; 81 } 82 vis[row]=i; 83 row=jump[row]; 84 } 85 } 86 int main(){ 87 //freopen("text.in","r",stdin); 88 //freopen("a.out","w",stdout); 89 scanf("%d%d",&n,&m); 90 for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",&a[i][j]); 91 work(1,n); 92 scanf("%d",&q); 93 int k,aa,b,e; 94 int row=1,col=1; 95 for(int i=1;i<=q;++i){ 96 scanf("%s",s); 97 if(s[0]=='m'){ 98 scanf("%d",&k); 99 while(col!=1&&k){k--;get(row,col);} 100 if(k>m){ 101 get_tarjan(row); 102 if(k>to_time*m){ 103 k-=to_time*m; 104 row=where_x; 105 k%=len*m; 106 } 107 } 108 while(col!=1&&k){k--;get(row,col);} 109 while(k>=m){k-=m;row=jump[row];} 110 while(k){k--;get(row,col);} 111 printf("%d %d\n",row,col); 112 } 113 else{ 114 scanf("%d%d%d",&aa,&b,&e);aa=_x(aa);b=_y(b);a[aa][b]=e; 115 Work(aa,b); 116 } 117 } 118 } 119 /* 120 3 6 121 204 889 21 921 229 601 122 465 187 937 481 241 147 123 882 201 8,581 301 457 829 124 20 125 change 126 3 3 581 127 change 128 1 1 951 129 move 130 46003169 131 move 132 93518379 133 move 134 44247980 135 move 136 20045342 137 change 138 1 1 661 139 move 140 37489600 141 move 142 95847680 143 move 144 74183070 145 move 146 290360 147 change 148 2 1 233 149 move 150 24935765 151 change 152 1 5 924 153 move 154 91540280 155 change 156 3 1 790 157 change 158 3 3 845 159 move 160 50868396 161 move 162 69816331 163 move 164 18735250 165 */ 166 /* 167 3 6 168 951 889 21 921 229 601 169 465 187 937 481 241 147 170 882 201 581 301 457 829 171 20 172 move 173 20045342 174 */ 175 /* 176 3 6 177 2 3 178 3 5 179 3 1 180 3 5 181 3 1 182 3 1 183 2 3 184 1 2 185 1 4 186 1 4 187 1 5 188 2 3 189 */ 碼風奇丑無比(贈一組數據)

?

2.可以發現每走一步相當于一次置換,所以可以用線段樹維護置換,置換滿足結合律,查詢可以使用快速冪,而修改只需要修改線段樹的一條鏈。這個我也沒打,具體看ex_face的blog

3.我自己YY的跑的超慢但是A掉的算法(其實是因為懶第一種不想打,笨第二種看不懂)。

題解中運用了循環節的思想,目的是將k模掉來降低復雜度,但是這樣復雜度取決與循環節長度比較不穩定,那怎么辦呢?可以沿用題解中jump數組的做法,而使用倍增,求出第一列每個點走$2^j*m$步到達的點,這樣查詢就是logk的,那修改呢?重新求倍增數組那個$n*log_m$是跑不了了,但是這樣的復雜度是可以接受的,但是jump數組怎么搞呢?暴力m*n求的話肯定會死,所以沿用第二種做法中線段樹的思想,每個節點維護一個jump數組,葉子節點jump[j]表示這列第j行走1步到達的行數,那么葉子節點的父親就表示左邊l列走2步到達的行數,上面的節點也一樣,那么對于每次修改,我們對其對應的葉子節點Om暴力修改,然后遞歸處理一條鏈,總復雜度$n*log_m$,之后重新求倍增數組,總復雜度可以接受。

1 #include<iostream> 2 #include<cstdio> 3 #define LL long long 4 #define MAXN 2010 5 #define int LL 6 #define lo 30 7 #define re register 8 using namespace std; 9 int n,m,Q; 10 struct jum 11 { 12 int jump[MAXN]; 13 void init() 14 {for(int i=1;i<=n;i++)jump[i]=i;} 15 }tmp; 16 void move(re int &x,re int &y); 17 void mul(jum &a,jum &b,jum &c) 18 {for(int i=1;i<=n;i++)c.jump[i]=b.jump[a.jump[i]];} 19 struct tree 20 { 21 jum j; 22 int l,r; 23 #define l(x) tr[x].l 24 #define r(x) tr[x].r 25 #define ls(x) (x<<1) 26 #define rs(x) (ls(x)+1) 27 #define j(x) tr[x].j 28 }tr[MAXN*10]; 29 void build(int x,int l,int r) 30 { 31 l(x)=l,r(x)=r; 32 if(l==r) 33 { 34 int tem=l; 35 for(int i=1;i<=n;i++)move(j(x).jump[i]=i,tem=l); 36 return; 37 } 38 int mid=(l+r)>>1; 39 build(ls(x),l,mid); 40 build(rs(x),mid+1,r); 41 mul(j(ls(x)),j(rs(x)),j(x)); 42 } 43 void add(int x,int t) 44 { 45 if(l(x)==r(x)) 46 { 47 int tem=t; 48 for(int i=1;i<=n;i++)move(j(x).jump[i]=i,tem=t); 49 return; 50 } 51 int mid=(l(x)+r(x))>>1; 52 if(t<=mid)add(ls(x),t); 53 else add(rs(x),t); 54 mul(j(ls(x)),j(rs(x)),j(x)); 55 } 56 int nowi=1,nowj=1; 57 int map[MAXN][MAXN]; 58 char op[10];int x,a,b,e; 59 int ju[MAXN][lo+5]; 60 inline int read(); 61 signed main() 62 { 63 // freopen("jump4.in","r",stdin); 64 65 n=read(),m=read(); 66 for(int i=1;i<=n;i++) 67 for(int j=1;j<=m;j++) 68 map[i][j]=read(); 69 70 Q=read(); 71 build(1,1,m); 72 for(int i=1;i<=n;i++) 73 ju[i][0]=j(1).jump[i]; 74 for(re int i=1;i<=lo;i++) 75 for(int j=1;j<=n;j++) 76 ju[j][i]=ju[ju[j][i-1]][i-1]; 77 for(int i=1;i<=Q;i++) 78 { 79 scanf("%s",op); 80 if(op[0]=='m') 81 { 82 x=read(); 83 while(nowj!=1&&x){move(nowi,nowj),x--;} 84 for(int k=lo;k>=0;k--) 85 if(1ll*(1<<k)*m<=x) 86 {nowi=ju[nowi][k],x-=1ll*(1<<k)*m;} 87 while(x){move(nowi,nowj),x--;} 88 printf("%lld %lld\n",nowi,nowj); 89 } 90 else 91 { 92 a=read(),b=read(),e=read(); 93 map[a][b]=e; 94 if(b==1)add(1,m); 95 else add(1,b-1); 96 for(int j=1;j<=n;j++) 97 ju[j][0]=j(1).jump[j]; 98 for(re int j=1;j<=lo;j++) 99 for(int k=1;k<=n;k++) 100 ju[k][j]=ju[ju[k][j-1]][j-1]; 101 } 102 } 103 } 104 inline int read() 105 { 106 int s=0;char a=getchar(); 107 while(a<'0'||a>'9')a=getchar(); 108 while(a>='0'&&a<='9'){s=s*10+a-'0',a=getchar();} 109 return s; 110 } 111 void move(re int &x,re int &y) 112 { 113 int t1,t2,t3; 114 if(y!=m) 115 { 116 t1=(x==1?map[n][y+1]:map[x-1][y+1]), 117 t2=map[x][y+1], 118 t3=(x==n?map[1][y+1]:map[x+1][y+1]); 119 } 120 else 121 { 122 t1=(x==1?map[n][1]:map[x-1][1]), 123 t2=map[x][1], 124 t3=(x==n?map[1][1]:map[x+1][1]); 125 } 126 if(t1>t2&&t1>t3) x--,y++; 127 else if(t2>t1&&t2>t3)y++; 128 else x++,y++; 129 if(x==0)x=n; 130 if(x==n+1)x=1; 131 if(y==0)y=m; 132 if(y==m+1)y=1; 133 } View Code

最后,附贈xuefeng的玄學亂搞騙到85分(不知道為啥我只能搞到80)的思路(以下純屬亂搞):

首先是20分的暴力一步一步地模擬(這個應該都會打吧),然后在輸入k后加一句話:if(k>10*m)k%=10*m,k+=2*m;看似很選學,實際上是有依據的,首先循環節肯定是m的整數倍,那為什么選10呢?借用wq學長的話:小了會WA大了會T。那加2*m又是為啥呢?因為在碰到循環節之前會走一個常數。

1 #include<bits/stdc++.h> 2 #define N 2005 3 #define LL long long 4 using namespace std; 5 int n,m,q; 6 int a[N][N]; 7 char s[6]; 8 inline int read(){ 9 LL x=0,f=1;char ch=getchar(); 10 while(!isdigit(ch))f=ch=='-'?-1:1,ch=getchar(); 11 while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); 12 return x*f; 13 } 14 void get(int &x,int &y){ 15 y=y+1==m+1?1:y+1;int xu=x-1==0?n:x-1,xd=x+1==n+1?1:x+1; 16 if(a[x][y]<a[xu][y])x=xu;if(a[x][y]<a[xd][y])x=xd; 17 } 18 int main(){ 19 n=read(),m=read(); 20 for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)a[i][j]=read(); 21 q=read();int k,A,b,e; 22 int row=1,col=1; 23 for(int i=1;i<=q;++i){ 24 scanf("%s",s); 25 if(s[0]=='m'){ 26 k=read(); 27 if(k>m*10)k%=m*10,k+=m*2; 28 while(k--)get(row,col); 29 printf("%d %d\n",row,col); 30 } 31 else{ 32 A=read(),b=read(),e=read(); 33 a[A][b]=e; 34 } 35 } 36 } 代碼也放一下吧

?

轉載于:https://www.cnblogs.com/Al-Ca/p/11306656.html

總結

以上是生活随笔為你收集整理的[***]HZOJ 跳房子的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。