[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\n", 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;
- }
[XJOI NOI2015模拟题13] A 神奇的矩阵 【分块】的更多相关文章
- [XJOI NOI2015模拟题13] C 白黑树 【线段树合并】
题目链接:XJOI - NOI2015-13 - C 题目分析 使用神奇的线段树合并在 O(nlogn) 的时间复杂度内解决这道题目. 对树上的每个点都建立一棵线段树,key是时间(即第几次操作),动 ...
- [XJOI NOI2015模拟题13] B 最小公倍数 【找规律】
题目链接:XJOI - NOI2015-13 - B 题目分析 通过神奇的观察+打表+猜测,有以下规律和性质: 1) 删除的 n 个数就是 1~n. 2) 当 c = 2 时,如果 n + 1 是偶数 ...
- XJOI NOIP模拟题2
第一题 组合计数 分析: 从前往后一位一位的计算 先算第一位比t小的数目,再算第一位与t[1]相同,第2位比t小的个数以此类推 先预处理一个数组h,h[i]表示从1~it串与s串不同的位数 对于第i位 ...
- XJOI NOIP模拟题1
第一题 分析: 开始想的是贪心,取每列均值最大一段. 应该是01分数规划,具体看代码 代码: program gold; var a:..]of int64; n,i,m,j,x:longint; f ...
- 【做题】HDU6331 Walking Plan——矩阵&分块
题意:给出一个有\(n\)个结点的有向图,边有边权.有\(q\)组询问,每次给出\(s,t,k\),问从\(s\)到\(t\)至少经过\(k\)条边的最短路. \(n \leq 50, \, q \l ...
- 神奇的矩阵 NOI模拟题
神奇的矩阵 题目大意 有一个矩阵\(A\),第一行是给出的,接下来第\(x\)行,第\(y\)个元素的值为数字\(A_{x-1,y}\)在\(\{A_{x-1,1},A_{x-1,2},A_{x-1, ...
- poj 1008:Maya Calendar(模拟题,玛雅日历转换)
Maya Calendar Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 64795 Accepted: 19978 D ...
- poj 1888 Crossword Answers 模拟题
Crossword Answers Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 869 Accepted: 405 D ...
- ZOJ1111:Poker Hands(模拟题)
A poker deck contains 52 cards - each card has a suit which is one of clubs, diamonds, hearts, or sp ...
随机推荐
- Android Studio中文组(中文社区)
Android Studio中文组(中文社区)http://www.android-studio.org/
- js数组求和
array1.reduce(callbackfn[, initialValue]) callback : 函数执行在数组中每个值 initialValue : 对象作为第一个参数回调的第一次调用使用 ...
- HTML5规范的本地存储
在HTML5 中定义了两种本地存储的,Web Storage 和本地数据库 SQL Database . 用来检查判断浏览器是否支持 Web Storage if(window.localStorag ...
- HTTP层 —— 请求
1.访问请求实例 要通过依赖注入获取当前 HTTP 请求实例,需要在控制器的构造函数或方法中对 Illuminate\Http\Request 类进行类型提示,这样当前请求实例会被服务容器自动注入: ...
- java多线程总结四:volatile、synchronized示例
1.synchronized保证同步 先看一个生成偶数的类 <span style="font-size:16px;">package demo.thread; /** ...
- ###再探Makefile
使用makefile.以前刚开始接触Makefile的时候,写过一个最简单的Makefile.点击查看Evernote原文. #@author: gr #@date: 2014-07-20 #@ema ...
- Flex布局新旧混合写法详解(兼容微信)
原文链接:https://www.usblog.cc/blog/post/justzhl/Flex布局新旧混合写法详解(兼容微信) flex是个非常好用的属性,如果说有什么可以完全代替 float 和 ...
- CSS多行文字截断
有时候容器的宽度是固定的,但要显示的文字有点多,就需要将多余的文字隐藏,而且为了表示还有字没有显示,用省略号表示. 类似这样: 单行文字 单行文字截断比较明显: .truncate { width: ...
- OpenJudge 2766 最大子矩阵
1.链接: http://bailian.openjudge.cn/practice/2766 2.题目: 总Time Limit: 1000ms Memory Limit: 65536kB Desc ...
- Yii 获取验证码与Yii常用的URL
$this->createAction('captcha')->getVerifyCode(); //获取当前验证码的值 当前页面url echo Yii::app()->requ ...