沈阳集训day6

Posted edsheeran

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了沈阳集训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

 

 



























以上是关于沈阳集训day6的主要内容,如果未能解决你的问题,请参考以下文章

暑假集训day6

湖南集训DAY6

北极集训DAY6

雅礼集训 Day6

「2017 山东一轮集训 Day6」子序列

省队集训 Day6 序列