• [XJOI NOI2015模拟题13] A 神奇的矩阵 【分块】


    题目链接:XJOI NOI2015-13 A

    题目分析

    首先,题目定义的这种矩阵有一个神奇的性质,第 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 <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <map>
    
    using namespace std;
    
    inline int gmax(int a, int b) {return a > b ? a : b;}
    
    inline void Read(int &Num)
    {
    	char c = getchar(); 
    	while (c < '0' || c > '9') c = getchar();
    	Num = c - '0'; c = getchar();
    	while (c >= '0' && c <= '9')
    	{
    		Num = Num * 10 + c - '0';
    		c = getchar();
    	}
    }
    
    map<int, int> M;
    
    const int MaxN = 100000 + 5, MaxNum = 200000 + 5, MaxB = 150 + 5;
    
    int n, m, k, Index, Blk, Tot;
    int A[MaxN], T[MaxN], Belong[MaxN], L[MaxB], R[MaxB], Sum[MaxB][MaxNum][2], Cnt[MaxNum];
    
    int Query2(int x)
    {
    	int ret = Sum[Belong[x] - 1][A[x]][0];
    	for (int i = L[Belong[x]]; i <= x; ++i)
    		if (A[i] == A[x]) ++ret;
    	return ret;
    }
    
    int Query3(int x)
    {
    	int Now, Num, ret;
    	Num = Query2(x);
    	ret = Sum[Belong[x] - 1][Num][1];
    	for (int i = L[Belong[x]]; i <= x; ++i)
    	{
    		++Cnt[A[i]];
    		Now = Sum[Belong[x] - 1][A[i]][0] + Cnt[A[i]];
    		if (Now == Num) ++ret;
    	}
    	for (int i = L[Belong[x]]; i <= x; ++i)
    		--Cnt[A[i]];
    	return ret;
    }
    
    int main()
    {
    	scanf("%d%d%d", &n, &m, &k);
    	Index = 0;
    	int Num;
    	for (int i = 1; i <= m; ++i) 
    	{
    		Read(Num);
    		if (M[Num] == 0) M[Num] = ++Index;
    		A[i] = M[Num];
    		T[i] = Num;
    	}
    	Blk = gmax((int)sqrt((double)m), m / 150);
    	for (int i = 1; i <= m; ++i)
    	{
    		Belong[i] = (i - 1) / Blk + 1;
    		if (L[Belong[i]] == 0) L[Belong[i]] = i;
    		R[Belong[i]] = i;
    	}
    	Tot = Belong[m];
    	for (int i = 1; i <= m; ++i)
    		for (int j = Belong[i]; j <= Tot; ++j)
    			++Sum[j][A[i]][0];
    	for (int i = 1; i <= m; ++i)
    	{
    		Num = Query2(i);
    		for (int j = Belong[i]; j <= Tot; ++j)
    			++Sum[j][Num][1];
    	}
    	int t, x, y, Ans;
    	for (int i = 1; i <= k; ++i)
    	{
    		Read(t); Read(x); Read(y);
    		if (t == 0)
    		{
    			if (x == 1) Ans = T[y];
    			else if (x & 1) Ans = Query3(y);
    			else Ans = Query2(y);
    			printf("%d
    ", Ans);
    		}
    		else
    		{
    			T[x] = y;
    			if (M[y] == 0) M[y] = ++Index;
    			y = M[y];
    			for (int j = Belong[x]; j <= Tot; ++j)
    				--Sum[j][Sum[j][A[x]][0]][1];
    			for (int j = Belong[x]; j <= Tot; ++j)
    				--Sum[j][A[x]][0];	
    			A[x] = y;
    			for (int j = Belong[x]; j <= Tot; ++j)
    				++Sum[j][A[x]][0];
    			for (int j = Belong[x]; j <= Tot; ++j)
    				++Sum[j][Sum[j][A[x]][0]][1];
    		}
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    901Air724UG模块(4G全网通GPRS开发)Air724UG(4G)使用SPI控制CH395Q(以太网模块)实现TCP/IP通信
    02STM32+MN316(NBIOT)基本控制篇(自建MQTT服务器)硬件使用说明
    Docker 清理none镜像 Prune命令
    Leetcode 252. 会议室 253. 会议室II 贪心算法扫描线技巧
    Leetcode 77. 组合(中等)回溯算法
    Leetcode 787. K 站中转内最便宜的航班(中等) 动态规划
    Leetcode 174. 地下城游戏(困难) 动态规划
    Linux arm环境下pip3安装av失败
    Leetcode 134. 加油站(中等) 贪心算法
    Leetcode 698. 划分为k个相等的子集(中等) 回溯算法=集合划分
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4596205.html
Copyright © 2020-2023  润新知