NOI2020训练题3 A 神奇的矩阵

题目描述:
有一个神奇的矩形。它的第一行每一个元素\(a_{1,i}\)都是给定的。对于每一个元素\(a_{x,y} \ (x>1)\),它的值都是\(a_{x−1,y}\)\(a_{x−1,1}, \dots,a_{x−1,y}\)中出现过的次数。但由于这个矩阵很大,人们并不开心这么慢吞吞地计算整个矩阵的值,因此他们找到了你,并要求你快速知道某一个位置的值。有时这个矩阵的第一行还会被修改,你当然也要考虑这些修改的因素。

输入格式:
输入文件的第一行有三个正整数\(n, m, k\),分别表示这个矩形的行数,列数和操作个数。

接下去一行\(m\)个数表示第一行的每一个数字。

接下去\(k\)行,每行的第一个正整数为\(t\),表示操作类型。

如果\(t=0\),则表示一次询问操作,接下去有两个正整数\(x,y\),表示询问矩阵中第\(x\)行第\(y\)列的元素的值。

否则\(t=1\),接下去有两个正整数\(y,v\),表示把第一行第\(y\)个数字的值改为\(v\)。矩阵中其他数字也会相应改变。

输出格式:

对于每一个询问,输出一个整数表示矩阵中对应的值。

样例输入1:

3 3 9
1 1 1
0 1 1
0 1 2
0 1 3
0 2 1
0 2 2
0 2 3
0 3 1
0 3 2
0 3 3

样例输出1:

1
1
1
1
2
3
1
1
1

样例输入2:

3 3 7
1 2 3
0 3 3
1 1 3
0 3 3
1 2 3
0 3 3
1 3 1
0 3 3

样例输出2:

3
1
1
2

数据范围:

20%的数据,nmk≤107.
博客
40%的数据,mk≤107.

对于另外30%的数据,t≠1.

100%的数据,1≤n,m,k≤105,1≤x≤n,1≤y≤m,1≤v,a1,i≤109.

时间限制: 2s
空间限制:256MB

转载博客

首先,题目定义的这种矩阵有一个神奇的性质,第 4 行与第 2 行相同,于是第 5 行也就与第 3 行相同,后面的也是一样。

因此矩阵可以看做只有 3 行,从上到下就是 1 2 3 2 3 2 3 ......

然后我们使用分块,将每一行分成 sqrt(m) 大小的块。

然后维护 A[i][j] —— 第一行前 i 块中,数字 j 的出现次数。

同时维护 B[i][j] —— 第二行前 i 块中,数字 j 的出现次数。

这里要将第一行的数字进行离散化减小 j 的范围。(同时要注意,询问第一行的数字时,不要直接输出了离散化之后的数字QAQ,要输出原本的数字,我就是这么WA的)

然后对于询问第二行的 x 位置,就先加上第一行 [1, x] 中前面的整个 k 块中这个数字的个数,再 O(sqrt n) 枚举最后一个块中前面到 x 的一段。

对于询问第三行的 x 位置,先计算第二行 x 位置的数值 Num ,加上第二行 [1, x] 中前面的整个 k 块中的 Num 个数,后面再求出最后一个块中前面到 x 的一段中有几个 Num,注意这里不能每个位置都 O(sqrt n) 求,而是 O(sqrt n) 扫一遍,同时用一个 Cnt[MaxNum] 的数组将扫到的数字对应的累加器+1,这样扫到一个位置就可以立即算出第二行这个位置的值了,最后再扫一遍将累加器减回去。

对于修改第一行的某个位置,显然可以向后扫每个块然后更新一下 A[][] 数组,然而 B[][] 的维护其实也是可以枚举后面的每个块然后总体 O(sqrt n) 维护的。

将修改操作分为插入和删除操作就可以很清晰地维护了。


#include<bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;
const int block = 1000;

int n,m,k;

int a[N];
int opt[N],l[N],r[N];

int lsh[N << 1], cnt;
int R;
int f[105][N << 1], g[105][N];

int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i = 1; i <= m; ++ i) scanf("%d",&a[i]), lsh[++ cnt] = a[i];
    
    for(int i = 1; i <= k; ++ i){
        scanf("%d%d%d",&opt[i],&l[i],&r[i]);
        if(opt[i] == 1) lsh[++ cnt] = r[i];
    }
    
    sort(lsh + 1, lsh + cnt + 1); cnt = unique(lsh + 1, lsh + cnt + 1) - lsh - 1;
    
    for(int i = 1; i <= m; ++ i) a[i] = lower_bound(lsh + 1, lsh + cnt + 1, a[i]) - lsh;
    for(int i = 1; i <= k; ++ i){
        if(opt[i] == 1) r[i] = lower_bound(lsh + 1, lsh + cnt + 1, r[i]) - lsh;
    }
    //lsh over
    
    R = (m - 1) / block + 1;
    
    for(int i = 1; i <= R; ++ i){
        int ll = (i - 1) * block + 1;
        int rr = min(i * block, m);
        
        memcpy(f[i],f[i - 1],sizeof(f[i - 1]));
        memcpy(g[i],g[i - 1],sizeof(g[i - 1]));
       
        for(int j = ll; j <= rr; ++ j){
            ++ f[i][a[j]]; ++ g[i][f[i][a[j]]];
        }
    }
    
    for(int i = 1; i <= k; ++ i){
        if(opt[i] == 0){
            if(l[i] == 1) printf("%d\n",lsh[a[r[i]]]);
            else{
                int x = (r[i] - 1) / block + 1;
                
                for(int j = (x - 1) * block + 1; j <= r[i]; ++ j){
                    ++ f[x - 1][a[j]];
                    ++ g[x - 1][f[x - 1][a[j]]];
                }
                printf("%d\n",(l[i] & 1) ? (g[x - 1][f[x - 1][a[r[i]]]]) : ( f[x - 1][a[r[i]]]) );
                for(int j = (x - 1) * block + 1; j <= r[i]; ++ j){
                    -- g[x - 1][f[x - 1][a[j]]];
                    -- f[x - 1][a[j]];
                }
            }
        }
        if(opt[i] == 1){
            int x = (l[i] - 1) / block + 1;
            for(int j = x; j <= R; ++ j){
                -- g[j][f[j][a[l[i]]]];
                -- f[j][a[l[i]]];
            }
            a[l[i]] = r[i];
            for(int j = x; j <= R; ++ j){
                ++ f[j][a[l[i]]];
                ++ g[j][f[j][a[l[i]]]];
            }
        }
    }
    
    return 0;
}

posted @ 2020-07-28 16:33  zhuzihan  阅读(248)  评论(0编辑  收藏  举报