前言
-
比賽連結
-
總分:\(107pts\)
-
\(T1~79pts:\)
座標 \(DP\) ,賽時感覺打的是正解,但是打假了。
-
\(T2~28pts:\)
理解錯題了,以為是幫他調程式了,於是給人家調 \(TLE\) 了。
-
\(T3~0pts,T4~0pts:\)
沒啥好說的,不會。
-
官方題解
T1 迴文
點選檢視題面
-
部分分:
部分分沒什麼好說的,大多集中在 \(79pts\) ,都是 \(DP\) 打假了的。
-
正解:
座標 \(DP\) 。
\(4\) 維的很好想,對於迴文串的性質,從前往後跑和從後往前跑路徑是一樣的。
那麼定義 \(f[i][j][k][l]\) ,表示從前往後跑到 \(a[i][j]\) ,從後往前跑到 \(a[k][l]\) 時的方案數。
但是 \(4\) 維肯定會 \(MLE\) ,之後發現第 \(4\) 維可以去掉,因為從前往後和從後往前跑路徑始終是保持相同的,所跑的步數也一定是相同的,那麼第 \(4\) 維 \(l\) 完全可以用 \(i,j,k\) 表示。
-
解釋一下:
從 \(a[1][1]\) 跑到 \(a[n][m]\) ,需要走的步數為 \(n+m-1\) ,起點不算,那麼均分下來,兩邊各需跑 \(maxx=\dfrac{n+m-1}{2}\) 步。
那麼對於從前往後的步數顯然為 \(i+j-1\) ,起點不算,那麼從後往前搜走的步數也一定是 \(i+j-1\) ;對於從後往前的步數同樣的可以表示為 \((n-k+1)+(m-l+1)-1=i+j-1\) ,那麼 \(l=m+n-i-j-k+2\) 。
所以定義 \(f[i][j][k]\) 即可,第 \(4\) 維不是消失了,是可以用前 \(3\) 維表示,不需要存了。
轉移方程很好想,如果 \(a[i][j]=a[k][l]\) ,那麼對於其下一步 \(f[i1][j1][k1]+=f[i][j][k]\) 即可。當然對於此時如果 \(i+j-1=maxx\) ,也就是已經匹配完了,就不需要轉移了。
不好搞的是統計答案。
發現 \(m+n\) 奇偶不同時,統計答案也是不同的,若 \(n+m\) 為奇數時,他兩邊同時跑最後時到達一個點的;反之為偶數時,他最後會跑到相鄰的兩個點。
如圖:
-
\(m+n\) 為奇數
討論到達 \(ans\) 的前一刻 \(k,l\) 可能的位置,如圖 \(1,2\) ,所以對於 \(k\) 的位置可能為 \(i,i+1\) ,\(l\) 的位置不用討論,\(k\) 確定同時也就確定了。
-
\(m+n\) 為偶數
討論 \(k,l\) 在到達 \(ans2\) 上一步時可能的位置,如圖 \(1,2,3\) ,同時注意 \(2\) 的貢獻有兩次,所以要家兩次。所以 \(k\) 的位置可能為 \(i,i+1,i+2\) ,其中 \(i+1\) 的情況要算兩遍。
而至於 \(\%\) 的常數很大,可以打一個 \(mod\) 函式,看程式碼就知道了。
點選檢視程式碼
#include<bits/stdc++.h> // #define int long long #define endl '\n' using namespace std; const int N=510,P=993244853; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int n,m,maxx; unsigned int f[N][N][N],ans; char a[N][N]; int hx[5]={0,1,1,0,0},zx[5]={0,0,0,1,1},hy[5]={0,-1,0,-1,0},zy[5]={0,0,-1,0,-1}; void mod(unsigned int &x) {x=(x>=P?x-P:x);} signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(n),read(m); maxx=(n+m-1)/2; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>a[i][j]; if(a[1][1]!=a[n][m]) { cout<<0; return 0; } f[1][1][n]=1; for(int i=1;i<=n;i++) for(int j=1;i+j<=maxx+1&&j<=m;j++) for(int k=n;k>=i;k--) { int l=m+n-i-j+2-k; if(a[i][j]==a[k][l]) { if(i+j!=maxx+1) for(int x=1;x<=4;x++) { int i1=i+hx[x],j1=j+zx[x],k1=k+hy[x],l1=l+zy[x]; if(a[i1][j1]==a[k1][l1]) f[i1][j1][k1]+=f[i][j][k],mod(f[i1][j1][k1]); } } } if(!((m+n)&1)) for(int i=1;i<=min(n,maxx);i++) { for(int k=0;k<=2;k++) { int j=maxx-i+1; ans+=f[i][j][i+k],mod(ans); } ans+=f[i][maxx-i+1][i+1],mod(ans); } else for(int i=1;i<=min(n,maxx);i++) for(int k=0;k<=1;k++) { int j=maxx-i+1; ans+=f[i][j][i+k],mod(ans); } cout<<ans; }
複雜度 \(O(n\times m\times n)\) 。
-
T2 快速排序
點選檢視題面
-
部分分:
直接在給的快排函式上調,他那個程式碼時沒有隨機化的,會 \(TLE\) 的,所以我 \(TLE~28pts\) ,別人多少不知道了。
-
正解:
首先他那個快排是死的,我們自帶的 \(stable\_sort\) 是有隨機化的,直接用這個即可。
然後分析他這個程式碼(分析樣例)。
對於當前位置如果是 \(nan\) 的話,就不動,直接輸出。
否則,就將所有比他小的數,包括他自己輸出即可,當然輸出過的就不再輸出了。
思路聽起來很簡單的,調程式碼就行了。
實現就比較簡單了,每個人方法可能不一樣,直接看程式碼吧。
點選檢視程式碼
#include<bits/stdc++.h> #define int long long #define endl '\n' using namespace std; const int N=5e5+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int t,n,b[N],tot,id[N]; string a[N]; bool v[N]; signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(t); while(t--) { tot=0; memset(v,0,sizeof(v)); memset(id,0,sizeof(id)); memset(b,0,sizeof(b)); read(n); for(int i=1;i<=n;i++) { cin>>a[i]; if(a[i]=="nan") continue; int s=0; for(int j=0;j<a[i].size();j++) s=s*10+a[i][j]-'0'; b[++tot]=s,id[i]=b[tot]; } stable_sort(b+1,b+1+tot); int j=0; for(int i=1;i<=n;i++) { if(a[i]=="nan") { cout<<a[i]<<' '; continue; } else { if(j==tot||b[j]>id[i]) continue; while(++j) { if(b[j]) cout<<b[j]<<' '; if(b[j]==id[i]||j==tot) break; } } } cout<<endl; } }
複雜度 \(O(n\log(n))\) ,帶一定的常熟(將字串轉換為整型)。