zoukankan      html  css  js  c++  java
  • 套题 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)维护一个单调队列;(太神奇了,,,)  我不会啊 

  • 相关阅读:
    JAVA基础集合(二)
    JAVA基础XML的解析
    JAVA基础集合(一)
    JAVA基础网络编程
    JAVA基础File类
    JAVA基础IO流(二)
    JAVA基础JDBC二(常用的开源工具)
    JAVA基础异常
    JAVA基础JDBC(一)
    JAVA基础反射
  • 原文地址:https://www.cnblogs.com/CLGYPYJ/p/7426299.html
Copyright © 2011-2022 走看看