套题 8.24
1、矩阵价值和(matrix.pas/c/cpp)
时间限制:4s;内存限制:256MB
【问题描述】
小 Y 有一个 n 行 n 列的螺旋矩阵。
一个 n 行 n 列的螺旋矩阵可由如下方法生成:
从矩阵的左上角(第 1 行第 1 列)出发,初始时向右移动;如果前方是未曾经过的格子,
则继续前进,否则右转;重复上述操作直至经过矩阵中所有格子。根据经过顺序,在格子
中 依次填入 1, 2, 3, ... , n2,便构成了一个螺旋矩阵。
下图是一个 n = 4 时的螺旋矩阵。
一个子矩阵的价值定义为其内部数总和的平方,下图红色子矩阵的价值为
(13+14+15+16)2
求所有子矩阵价值
【输入文件】
一个整数 n,表示矩阵大小。
【输出文件】
输出所有子矩阵价值的总和,答案对 1000000007 取模。
【样例输入】
3
【样例输出】
11744
【数据范围】
对于 30%的数据,n<=20
对于 60%的数据,n<=80
对于 100%的数据,n<=3000
思路:
考虑一个数的平方(a+b+c.....)*(a+b+c+.....)
= a^2+b^2+c^2....+2ab+2ac+2bc....
=2ab+2ac+2bc+2a*a+2*b*b+2c*c-a*a-b*b-c*c。。。
所以我么只需要考虑类似 “ 2*a*b ”的式子,各自出现的次数,把它们加起来,最后减去a*a ,b*b,c*c 。。。就是最终答案。
对于矩阵中的任意两个数,2*a*b 他们出现的次数是i*j*(n-i+1)*(n-j+1).
或者i*(n-i+1)*j*(n-j+1).
用区间前缀和维护,复杂度是N*N。
不知哪错了,60分
#include<iostream> #include<cstdio> #include<queue> #include<vector> #include<algorithm> #include<cstring> #include<math.h> #define MOD 1000000007 using namespace std; typedef long long LL; int n,map[3009][3009]; bool vis[3009][3009]; LL ans,tot; LL f[3009][3009]; LL mo(LL a) {if(a>=0 && a<MOD)return a;a%=MOD;if(a<0)a+=MOD;return a;} int main() { freopen("matrix.in","r",stdin); // freopen("matrix.out","w",stdout); scanf("%d",&n); for(int i=0;i<=n+1;i++) { vis[0][i]=vis[n+1][i]=1; vis[i][0]=vis[i][n+1]=1; } int x=1,y=1; map[1][1]=1,tot=1;vis[1][1]=1; while(tot < (LL)(n*n)) { while(!vis[x][y+1]) map[x][++y]=++tot,vis[x][y]=1; while(!vis[x+1][y]) map[++x][y]=++tot,vis[x][y]=1; while(!vis[x][y-1]) map[x][--y]=++tot,vis[x][y]=1; while(!vis[x-1][y]) map[--x][y]=++tot,vis[x][y]=1; } /* for(int i=1;i<=n;i++,cout<<endl) printf("%d ",map[i][1]); */ for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { LL t=map[i][j]; ans=mo(ans-t*i*j%MOD*t%MOD*(n-i+1)%MOD*(n-j+1)); f[i][j]=mo(t*i%MOD*j%MOD+f[i-1][j]+f[i][j-1]-f[i-1][j-1]); ans=(ans+2*f[i][j]*t%MOD*(n-i+1)%MOD*(n-j+1)%MOD)%MOD; } //printf("%lld\n",ans); for(int i=1;i<=n;i++) for(int j=n;j>=1;j--) { LL t=map[i][j]; f[i][j]=(f[i-1][j]+f[i][j+1]-f[i-1][j+1]+1LL*t*i%MOD*(n-j+1)%MOD+MOD)%MOD; ans=(ans+2LL*t*(n-i+1)%MOD*j*f[i-1][j+1]%MOD+MOD)%MOD; } printf("%lld\n",ans); return 0; }
2、区间第 k 大(kth.pas/c/cpp)
时间限制:1s;内存限制:256MB
【问题描述】
一个区间的价值定义为该区间中的最大值减最小值给定 n 个数,求所有区间价值中,
第 k 大值为多少。
【输入文件】
第 1 行两个数 n、k(k<=n*(n-1)/2)
第 2 行 n 个数,每个数<=109
【输出文件】
输出区间价值的第 k 大值。
【样例输入】
3 2
2 1 3
【样例输出】
2
【样例解释】
[l,r]表示第 l 个数到第 r 个数组成的区间的价值
[1,1]=1 [1,2]=1 [1,3]=2
[2,2]=1 [2,3]=2
[3,3]=1
其中第二大的为 2
【数据范围】
对于 30%的数据,n=500
对于 80%的数据,n<=5000
对于 100%的数据,n<=400000
思路:
先二分答案ans,然后判断有多少个区间的价值比ans大
如何找有多少个区间的价值比ans大?
固定区间右端点,找到最大的L,使得【L,R】 的价值>=ans, 那么对于左端点小于L,右端点等于R的所有区间,其价值一定>=ans.
所以tot+=L。 最后比较tot 和 k就行了。
如何维护某一区间内的最大值和最小值?
有两种思路
(1)st表;
f1[i][j]表示区间【i,2^j-1】的最小值,f2[i][j]维护最大值
这样就避免了用 f[i][j]表示区间【i,j】的最值时,空间不够用的问题。
(2)维护一个单调队列;(太神奇了,,,) 我不会啊