套题 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
",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
",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)维护一个单调队列;(太神奇了,,,)  我不会啊 

原文地址:https://www.cnblogs.com/CLGYPYJ/p/7426299.html