牛客 - 捡金币(思维+二维前缀和+构造)
題目鏈接:點擊查看
題目大意:給出一個n*m的矩陣,每個方格都有一個權值,現在給出q次詢問,每次詢問的格式是x,y,k,問與點(x,y)的曼哈頓距離不超過k的方格內的所有權值之和
題目分析:首先這個題目肯定是不能暴力的,如果暴力跑的話時間復雜度就是n*m*q,大概1e11左右。。因為詢問的次數已經達到了1e5,加上樣例會給100組,時間復雜度已經到達了1e7,所以這個題目肯定是要求我們每次詢問O(1)輸出才能達到效果,說道O(1)輸出,最普遍的方法是對于每組數據預處理打個表,打表的話也不能太暴力,我們該想一個合適的方法來打表
其實看到求一個范圍內的權值和,第一反應肯定是二維前綴和,但在紙上畫了畫所要求的范圍發現,二維前綴和直接寫的話不太好實現,因為他是一個旋轉45度之后的正方形,既然都說到了旋轉45度,那我們直接將矩陣旋轉45度存起來不就好了嘛
現在問題轉換為了如何將整個圖旋轉45度后存起來,首先就要想到對角線,以對角線為橫縱坐標即可,原始坐標為(x,y),那么主對角線上的x軸我們可以用x+y來表示,那么副對角線該怎么表示呢,如果是x-y的話就成負數了,這個時候只要將整體向右平移m個單位就好了,因為y的范圍是1~m,所以x-y+m一定是一個正數,這樣一來就可以將整個矩陣旋轉45度后存起來了,對于這個新的矩陣求一下二維前綴和,然后就可以直接O(1)查詢了
有幾個細節需要注意一下,查詢的時候注意邊界問題,我們新構造的矩陣,范圍直接開到(m+n)*(m+n)即可,最好在處理之前輸出一下儲存的矩陣看一下是不是自己理想中的狀態,然后再做后續處理,然后就是新坐標和舊坐標的映射關系了,這個沒什么好說的,剩下的就是簡單實現了
代碼:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> #include<unordered_map> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=2e3+100;int n,m;LL maze[N][N];LL cal(int x,int y,int k)//求前綴和 {int xx=x+y;int yy=x-y+m;int upx=min(xx+k,m+n);int downx=max(xx-k,1);int upy=min(yy+k,m+n);int downy=max(yy-k,1);return maze[upx][upy]-maze[downx-1][upy]-maze[upx][downy-1]+maze[downx-1][downy-1]; }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);int w;cin>>w;while(w--){memset(maze,0,sizeof(maze));scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)//構造矩陣for(int j=1;j<=m;j++)scanf("%lld",&maze[i+j][i-j+m]);for(int i=1;i<=n+m;i++)//二維前綴和for(int j=1;j<=m+n;j++)maze[i][j]+=maze[i-1][j]+maze[i][j-1]-maze[i-1][j-1];int q;scanf("%d",&q);while(q--){int x,y,k;scanf("%d%d%d",&x,&y,&k);printf("%lld\n",cal(x,y,k));}}return 0; }?
總結
以上是生活随笔為你收集整理的牛客 - 捡金币(思维+二维前缀和+构造)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 牛客 - 排序(模拟)
- 下一篇: POJ - 2112 Optimal M