• 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
    ",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
    ",(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;
    }
    
    
  • 相关阅读:
    linux vsftp配置
    oracle 执行计划查看
    oracle更新语句merge和update
    windows server2008 kettle部署
    oracle-trasnlate函数
    oracle 报警日志详解
    修改oracle实例名orcl为demo
    2017.11.05
    2017.10.22
    2017.09.22
  • 原文地址:https://www.cnblogs.com/zzhzzh123/p/13391911.html
Copyright © 2020-2023  润新知