题解[下棋]
题意
螺旋状棋盘(如下),棋子一开始在格子1,q次询问,每次询问x,表示棋子往前走x步(循环,n*n->1),且分数加上行号和当前行号距离<x或列号和当前列号距离<x的格子的编号之和,输出每次走完的分数。
题解
关键词
预处理,二维前缀和
做法一
首先处理出编号i与行和列的关系,即求出a[x][y]为x行y列的编号,b[i]为编号i的行,c[i]为编号i的列。
接下来模拟游戏过程,每次暴力把合法的格子的编号加上即可。
复杂度 ,预计通过30%。其中k=6,是骰子的最高点数。
做法二
可以发现每次加上的格子由一些行和一些列组成,那么我们可以预处理出每行和每列的和,然后模拟时加上这些行和列的和。
注意需要减去重复的格子,但每次这些格子最多只有11*11个。
复杂度 ,预计通过100%。
做法三
也可以求一个二维前缀和。令。
sum数组可以dp进行求解:
然后假设矩形为左上角,右下角,那么这个矩形的和为: 。
这样我们可以时间求得一个矩形的和。而每次加上的分数可以分解为不超过3个矩形。
复杂度 ,预计通过100%
代码
做法一
#include <bits/stdc++.h> using namespace std; #define ll long long const int maxn = 2020; const int mod = 1000000007; ll a[maxn][maxn], b[maxn*maxn], c[maxn*maxn]; int dx[4]={0,1,0,-1}; int dy[4]={1,0,-1,0}; class Solution { public: /** * 求每一步后的总分数 * @param n int整型 棋盘大小 * @param query int整型vector 每次掷出的点数的列表 * @return long长整型vector */ vector<int> getScores(int n, vector<int>& query) { for (int i=0;i<=n+1;i++) for (int j=0;j<=n+1;j++) a[i][j]=0; for (int i=0;i<=n*n+1;i++) b[i]=c[i]=0; int x=1,y=1,dir=0; for (int i=1;i<=n*n;i++) { a[x][y]=i,b[i]=x,c[i]=y; if (i<n*n) for (;;dir=(dir+1)%4) { int nx=x+dx[dir],ny=y+dy[dir]; if (1 > nx || nx > n || 1 > ny || ny > n || a[nx][ny]) continue; x=nx,y=ny; break; } } vector <int> result; int now = 1; ll ans = 0; int m = query.size(); for (int i=0;i<m;i++) { int x = query[i]; now = (now + x - 1)%(n*n)+1; ll tmp = 0; int l=max(1ll,c[now]-x+1), r=min((ll)n,c[now]+x-1); for (int i=1;i<=n;i++) for (int j=l;j<=r;j++) tmp += a[i][j]; int l2=max(1ll,b[now]-x+1), r2=min((ll)n,b[now]+x-1); for (int i=l2;i<=r2;i++) { for (int j=1;j<l;j++) tmp += a[i][j]; for (int j=r+1;j<=n;j++) tmp += a[i][j]; } ans += tmp; result.push_back(ans%mod); } return result; } };
做法二
询问前处理行和和列和,具体来说,后一段改成这样:
static ll d[maxn], e[maxn]; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) d[i]+=a[i][j], e[j]+=a[i][j]; vector <int> result; int now = 1; ll ans = 0; int m = query.size(); for (int i=0;i<m;i++) { int x = query[i]; now = (now + x - 1)%(n*n)+1; ll tmp = 0; int l=max(1ll,c[now]-x+1), r=min((ll)n,c[now]+x-1); int l2=max(1ll,b[now]-x+1), r2=min((ll)n,b[now]+x-1); for (int i=l;i<=r;i++) tmp += e[i]; for (int i=l2;i<=r2;i++) tmp += d[i]; for (int i=l2;i<=r2;i++) { for (int j=l;j<=r;j++) tmp -= a[i][j]; } ans += tmp; result.push_back(ans%mod); }
做法三
预处理二维前缀和,具体如下:
#include <bits/stdc++.h> using namespace std; #define ll long long const int maxn = 2020; const int mod = 1000000007; ll a[maxn][maxn], b[maxn*maxn], c[maxn*maxn]; int dx[4]={0,1,0,-1}; int dy[4]={1,0,-1,0}; class Solution { public: ll ask(int l,int r,int l2,int r2,int n) { l2=min(l2,n), r2=min(r2,n); l=max(l,1), r=max(r,1); if (l2 < l || r2 < r) return 0; return a[l2][r2]-a[l-1][r2]-a[l2][r-1]+a[l-1][r-1]; } /** * 求每一步后的总分数 * @param n int整型 棋盘大小 * @param query int整型vector 每次掷出的点数的列表 * @return long长整型vector */ vector<int> getScores(int n, vector<int>& query) { for (int i=0;i<=n+1;i++) for (int j=0;j<=n+1;j++) a[i][j]=0; for (int i=0;i<=n*n+1;i++) b[i]=c[i]=0; int x=1,y=1,dir=0; for (int i=1;i<=n*n;i++) { a[x][y]=i,b[i]=x,c[i]=y; if (i<n*n) for (;;dir=(dir+1)%4) { int nx=x+dx[dir],ny=y+dy[dir]; if (1 > nx || nx > n || 1 > ny || ny > n || a[nx][ny]) continue; x=nx,y=ny; break; } } for (int i=1;i<=n;i++) { for (int j=1;j<=n;j++) { a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1]; } } int now = 1; ll ans = 0; vector <int> result; int m = query.size(); for (int i=0;i<m;i++) { x = query[i]; now = (now + x - 1)%(n*n)+1; ll tmp = ask(1,c[now]-x+1,n,c[now]+x-1,n) + ask(b[now]-x+1,1,b[now]+x-1,n,n) - ask(b[now]-x+1,c[now]-x+1,b[now]+x-1,c[now]+x-1,n); ans += tmp; result.push_back(ans%mod); } return result; } };