• 沈阳集训day6


    问题 A: YY的矩阵

    题目描述

    YY有一个大矩阵(N*M), 矩阵的每个格子里都有一个整数权值W[i,j](1<=i<=M,1<=j<=N)
    对于这个矩阵YY会有P次询问,每次询问这个大矩阵的一个子矩阵内的最大值。

    输入

    第一行两个整数N和M。
    接下来N行,每行M个整数
    然后,一行是整数P;
    接下来P行,每行4个整数r1, c1, r2, c2(分别表示子矩阵的左上角坐标和右下角坐标)

    输出

    共P行,每行一个整数,表示相应的最大值。

    样例输入

    4 4
    4 4 10 7
    2 13 9 11
    5 7 8 20
    13 20 8 2
    4
    1 1 4 4
    1 1 3 3
    1 3 3 4
    1 1 1 1

    样例输出

    20
    13
    20
    4

    提示

    数据范围:

    60%的数据:N×M×P<10^8

    100%的数据:1 <= N, M <= 300;1 <= P <= 1,000,000;1 <= r1 <= r2 <= N, 1 <= c1 <= c2 <= M,1<=W[i][j]<=10000.

    题解:二维ST表,O(1)查询,log要开成N那么大

    #include <bits/stdc++.h>
     
    using namespace std;
    int n, m, a[305][305][10][10], lo[310];
    void init(){
        for(int pi = 0; pi < 10; pi++)
            for(int pj = 0; pj < 10; pj++)
                if(!pi && !pj)continue;
                else for(int i = 1; i + (1<<pi) -1 <= n; i++)
                    for(int j = 1; j + (1<<pj) -1 <= m; j++){
                        if(!pi)
                            a[i][j][pi][pj] = max(a[i][j][pi][pj - 1], a[i][j + (1<<(pj-1))][pi][pj - 1]);
                        else
                            a[i][j][pi][pj] = max(a[i][j][pi - 1][pj], a[i + (1<<(pi-1))][j][pi - 1][pj]);
                       // printf("%d %d %d %d %d
    ", i,j,pi,pj,a[i][j][pi][pj]);
                    }
     
    }
    int query(int x1, int y1, int x2, int y2){
        int pi = lo[x2 - x1 + 1], pj = lo[y2 - y1 + 1];
        int ans1 = max(a[x1][y1][pi][pj], a[x2 - (1<<pi) +1][y1][pi][pj]);
        int ans2 = max(a[x1][y2 - (1<<pj) + 1][pi][pj], a[x2 - (1<<pi) + 1][y2 - (1<<pj) + 1][pi][pj]);
        return max(ans1 ,ans2);
     
    }
     
    int main()
    {
        scanf("%d%d", &n,  &m);
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                scanf("%d", &a[i][j][0][0]);
        lo[0] = -1;
        for(int i = 1; i <= 305; i++)lo[i] = lo[i/2] + 1;
        init();
        int q;
        scanf("%d", &q);
        while(q--){
            int x1, x2, y1, y2;
            scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
            int ans = query(x1, y1, x2, y2);
            printf("%d
    ", ans);
        }
        return 0;
    }
    View Code

    问题 B: WYT的刷子(一道单调队列的好题)

    题目描述

    WYT有一把巨大的刷子,刷子的宽度为M米,现在WYT要使用这把大刷子去粉刷有N列的栅栏(每列宽度都为1米;每列的高度单位也为米,由输入数据给出)。
    使用刷子的规则是:
    1、与地面垂直,从栅栏的底部向上刷
    2、每次刷的宽度为M米(当剩余栅栏宽度不够M米的话,刷子也可以使用,具体看样例2)
    3、 对于连续的M列栅栏,刷子从底向上,刷到的高度只能到这M列栅栏的最低高度。
    WYT请你回答两个问题:
    1、最少有多少个单位面积不能刷到(单位面积为1平米)
    2、在满足第一问的条件下,最少刷几次?

    输入

    共两行:
    第一行两个整数N和M。
    第二行共N个整数,表示N列栅栏的高度

    输出

    两行,每行一个整数,分别为最少剩余的单位面积数量和最少刷的次数。

    样例输入

    Input1:
    5 3
    5 3 4 4 5
    Input2:
    10 3
    3 3 3 3 3 3 3 3 3 3
    Input3:
    7 4
    1 2 3 4 3 2 1

    样例输出

    Output1:
    3
    2
    Output2:
    0
    4
    Output3:
    4
    4

    提示

    样例1的解释:



    数据范围:

    30%的数据:N<=10^3

    50%的数据:N<=10^5

    100%的数据:1<=N<=10^6, 1<=M<=10^6,N>=M, 每列栅栏的高度<=10^6.

    题解:两个单调队列,一个维护区间内最小值,一个在区间内维护最大值,就可以得到可以涂的轮廓线;(自己画图)

    遇到高度不一样或长度不够则要多一个刷子;

    单调对列求区间最大用递减队列;

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int M = 1e6+10;
    struct Queue{
        int id; ll h;
    }q[M], q2[M], q3[M];
     
    ll a[M], lim[M];
     
    int main(){
        int n, m; ll ret = 0;
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++)scanf("%lld", &a[i]);
        int h = 1, t = 0;
        for(int i = 1; i <= n; i++){
            while(h <= t && q[t].h >= a[i])t--;
            q[++t].id = i; q[t].h = a[i];
            if(i >= m){
                if(q[h].id <= i - m)h++;
                q2[i].h = q[h].h; q2[i].id = q[h].id;
            }
        }
        for(int i = 1; i < m; i++)q2[i].id = i, q2[i].h = -1e8;
        h = 1, t = 0;
        for(int i = 1; i <= n; i++){
            while(h <= t && q3[t].h <= q2[i].h)t--;
            q3[++t].id = i; q3[t].h = q2[i].h;
            if(i >= m){
                if(q3[h].id <= i - m)h++;
                lim[i - m + 1] = q3[h].h;
                ret += a[i - m + 1] - lim[i - m + 1];
            }
        }
        ll tt = q2[n].h;
        for(int i = n; i > n - m + 1; i--){
            tt = max(tt, q2[i].h);
            lim[i] = tt;
            ret += a[i] - lim[i];
        }
        //for(int i = 1; i <= n; i++)printf("%d ", lim[i]);
        int lst = 1, cnt = 1;
        for(int i = 1; i <= n; i++){
            if(i - lst + 1 > m || lim[lst] != lim[i]){
                lst = i; cnt++;
            }
        }
        printf("%lld
    %d
    ", ret, cnt);
    }
    View Code

    问题 C: 2017种树

    题目描述

    2017共有N棵树从0到N-1标号。现要把这些树种在一条直线上,第i棵树的种植位置X[i]如下确定:
    X[0] = X[0] MOD L;
    X[i] = (X[i-1]*A+B) MOD L。
    每棵树种植的费用,是所有标号比它小的树与它的距离之和。2017请你计算各棵树的费用之积,最后对1000000007取余。

    输入

    共五行:
    第一行为N
    第二行为L
    第三行为X[0]
    第四行为A
    第五行为B

    输出

    总费用

    样例输入

    5
    10
    3
    1
    1
    

    样例输出

    180

    提示

    样例解释:

    5棵树的位置分别为: 3, 4, 5, 6, 7. 

    费用分别为: 1, 3, 6, 10. (从第一棵树开始)

    总费用为: 1 × 3 × 6 × 10 = 180.

    数据范围:

    10%的数据:N<=10;

    60%的数据:N<=5×10^4;

    100%的数据:N,L<=200000; X[0] ,A, B<=10^9.

     题解:值域线段树

    一个树与其他树距离和:他离开头的距离*在他前面树的个数 - 前面树离开头距离总和 + 他离结尾的距离*在他后面树的个数 - 后面树离结尾距离总和;用值域线段树记录前面树的个数距离,log(N)求一棵树离其他树的距离,总复杂度Nlog(N);

    注意到处都要mod, 还要防止减成负数,还有nd->zuo = (nd->zuo + pos) % mod; 不能直接赋值成位置,因为有多个树,这个地方wa了很久

    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int M = 200005;
    const ll mod = 1000000007;
    int cnt, m;
    ll dis[M], tmp[M];
    ll ans = 1;
    struct Node{
        ll zuo, you;
        int zz;
        Node *ls, *rs;
     
        void up(){
            zuo = (ls->zuo + rs->zuo) % mod;
            you = (ls->you + rs->you) % mod;
            zz = ls->zz + rs->zz;
            //yy = ls->yy + rs->yy;
        }
    }pool[M<<2], *root, *tail = pool;
    Node * build(int l = 1, int r = m){
        Node * nd = ++tail;
        if(l == r)nd->zuo = nd->you = nd->zz = 0;
        else {
            int mid = (l + r) >> 1;
            nd->ls = build(l, mid);
            nd->rs = build(mid+1, r);
            nd->up();
        }
        return nd;
    }
    #define Ls nd->ls, l, mid
    #define Rs nd->rs, mid+1, r
    ll query1(int L, int R,  Node * nd = root, int l = 1, int r = m){
        if(L <= l && R >= r){
            cnt += nd->zz;
            return nd->zuo;
        }
        int mid = (l + r) >> 1;
        ll ans = 0;
        if(L <= mid) ans = query1(L, R, Ls);
        if(R > mid) ans = (ans+ query1(L, R, Rs)) % mod;
        return ans;
    }
    ll query2(int L, int R,  Node * nd = root, int l = 1, int r = m){
        if(L <= l && R >= r){
            cnt += nd->zz;
            return nd->you;
        }
        int mid = (l + r) >> 1;
        ll ans = 0;
        if(L <= mid) ans = query2(L, R, Ls);
        if(R > mid) ans = (ans+ query2(L, R, Rs)) % mod;
        return ans;
    }
    void insert(int pos, ll d2, Node * nd = root, int l = 1, int r = m){
        if(l == r){
            nd->zz ++;
            nd->zuo = dis[pos];
            nd->you = d2;
        }
        else {
            int mid = (l + r) >> 1;
            if(pos <= mid)insert(pos, d2, Ls);
            else insert(pos, d2, Rs);
            nd->up();
        }
    }
    int main()
    {
        int n;
        ll L, A, B;
        scanf("%d%lld%lld%lld%lld", &n, &L, &dis[1], &A, &B);
        dis[1] %= L;
        tmp[1] = dis[1];
        for(int i = 2; i <= n; i++)tmp[i] = dis[i] = (dis[i-1]*A+B) % L;
       // cout<<"OO";
        sort(tmp+1, tmp+1+n);
        m = unique(tmp+1, tmp+1+n) - tmp - 1;
        root = build();
      //  cout<<"h1"<<endl;
        for(int i = 1; i <= n; i++){
            int pos = lower_bound(tmp+1, tmp+1+m, dis[i]) - tmp;
            ll here = 0;
            cnt = 0;
            ll a1 = query1(1, pos);
         //   cout<<"L";
            here = ( (1LL*cnt*dis[i] % mod - a1) + mod * 2 ) % mod;
            cnt = 0;
            ll a2 = query2(pos, m);
        //    cout<<"KK";
            ll dd = tmp[m] - dis[i];
            here = (here + ( (1LL*cnt*dd%mod - a2) % mod) + mod * 2) % mod;
            if(!here && i == 1)here = 1;
            insert(pos, dd);
            ans = (ans * here) % mod;
            //cout<<ans<<endl;
        }
        printf("%lld
    ", ans);
        return 0;
    }
    View Code

     

  • 相关阅读:
    【基础】Oracle 表空间和数据文件
    oracle学习笔记1:windows下oracle数据库安装及.net调用oracle数据库
    redis中文网站
    .NET中的消息队列
    .Net下的进程间的通讯 -- Windows消息队列
    1060 最复杂的数(反素数玄学dfs)
    1282 时钟(最小表示法+hash)
    1191 消灭兔子(贪心+优先队列)
    1366 贫富差距(floyed)
    1503 猪和回文(DP)
  • 原文地址:https://www.cnblogs.com/EdSheeran/p/9277380.html
Copyright © 2020-2023  润新知