P5048 [[Ynoi2019模拟赛]Yuno loves sqrt technology III]

为什么我感觉这题难度虚高啊……

区间众数的出现次数…

计算器算一下 \(\sqrt 500000 = 708\)

然后我们发现这题的突破口?

考虑分块出来[L,R]块的众数出现个数 用 \(\texttt{mx[L][R]}\) 维护就可以了

每次考虑一个L… 然后R指针一直向右移不断的更新到N 这样子做的复杂度因为最多有 \(\sqrt n\) 个块 所以复杂度大概是 \(n\sqrt n\) 实际上还少一点…

然后整块的想好了……单独的怎么处理?

分类讨论

1 \(\texttt{L}\)和\(\texttt{R}\) 所在的块相同 那么分块的常规暴力操作(记得清空…)复杂度保证是 \(\sqrt n\)的

2 不在一个块的话 考虑用一种其他方法…记录一个\(\texttt{v[i]}\)存的是每个 数值 i 的出现位置 再记录一个 \(\texttt{pos[i]}\) 表示 i 这个数值在序列里是第几次出现…(主要还是方便vector的操作…)
您已经统计出来了 \(\texttt{L-R}\) 的最多次数 肯定是保底 \(\texttt{mx[L][R]}\) 了 根据这个基础 这样指针移动就相对来说保证了复杂度…每次也是\(\sqrt n\)的

讲下具体操作 记录了这个 \(pos_i\) 是 i 在 \(a_i\) 第几个出现 然后 \(v_{a_i}\)是记录了每个 \(a_i\)的出现位置 于是就可以 在左半区间的时候判断是否\(\leq \texttt{R}\) 在右半区间的时候判断是否\(\ge\texttt{L}\) 如果满足条件就加大 当前的\(\texttt{ans}\) 直到不满足 肯定是最优的…

#include<bits/stdc++.h>
using namespace std ;
const int N = 5e5 + 10 ;
int n , m , a[N] , b[N] , bl[N] , unt = 0 , L[710] , R[710] , mx[710][710] , tot[N] , pos[N] ;
vector < int > v[N] ;
inline int query(int l , int r) { int ans = 0 ;
  if(bl[l] == bl[r]) {
    for(register int i = l ; i <= r ; i ++) tot[a[i]] = 0 ;
    for(register int i = l ; i <= r ; i ++) ans = max(ans , ++ tot[a[i]]) ;
    return ans ;
  } ans = mx[bl[l] + 1][bl[r] - 1] ;
  for(register int i = l ; i <= R[bl[l]] ; i ++) {
    int it = pos[i] ; while(it + ans < v[a[i]].size() && v[a[i]][it + ans] <= r) ++ ans ;
  }
  for(register int i = L[bl[r]] ; i <= r ; i ++) {
    int it = pos[i] ; while(it - ans >= 0 && v[a[i]][it - ans] >= l) ++ ans ;
  } return ans ;
}
signed main() {
  // freopen("0.in" , "r" , stdin) ;
  ios :: sync_with_stdio(false) ;
  cin.tie(0) ;cout.tie(0) ;
  cin >> n >> m ;
  for(register int i = 1 ; i <= n ; i ++) { cin >> a[i] ; b[i] = a[i] ; }
  sort(b + 1 , b + n + 1) ;
  int len = unique(b + 1 , b + n + 1) - b - 1 ;
  for(register int i = 1 ; i <= n ; i ++) { a[i] = lower_bound(b + 1 , b + len + 1 , a[i]) - b ; }
  for(register int i = 1 ; i <= n ; i ++) { v[a[i]].push_back(i) ; pos[i] = v[a[i]].size() ; pos[i] -- ; }
  int unt = sqrt(n) ;
  for(register int i = 1 ; i <= n ; i ++) bl[i] = (i - 1) / unt + 1 ;
  for(register int i = 1 ; i <= bl[n] ; i ++) { L[i] = (i - 1) * unt + 1 ; R[i] = i * unt ; }
  R[bl[n]] = n ;
  for(register int i = 1 ; i <= bl[n] ; i ++) {
    memset(tot , 0 , sizeof(tot)) ;
    for(register int j = i ; j <= bl[n] ; j ++){
      mx[i][j] = mx[i][j - 1] ;
      for(register int k = L[j] ; k <= R[j] ; k ++) mx[i][j] = max(mx[i][j] , ++ tot[a[k]]) ;
    }
  }
  int lastans = 0 ;
  for(register int i = 1 ; i <= m ; i ++) {
    int l , r ; cin >> l >> r ;
    l ^= lastans ; r ^= lastans ;
    if(l > r) swap(l , r) ;
    cout << (lastans = query(l , r)) << '\n' ;
  }
  return 0 ;
}

原文地址:https://www.cnblogs.com/Isaunoya/p/11842586.html

时间: 2024-10-09 19:50:44

P5048 [[Ynoi2019模拟赛]Yuno loves sqrt technology III]的相关文章

[Luogu]5048 [Ynoi2019模拟赛]Yuno loves sqrt technology III[分块]

题意 长为 \(n\) 的序列,询问区间众数,强制在线. \(n\leq 5\times 10^5\). 分析 考虑分块,暴力统计出整块到整块之间的众数次数. 然后答案还可能出现在两边的两个独立的块中,开 \(vector\) 记录每种数字出现的位置集合,然后暴力判断两边两个块中的元素出现的次数.发现并不需要知道具体在 \([l,r]\) 内出现了多少次,而只需要知道 \([l,r]\) 中是否有 \(ans+1\)个该种颜色. 总时间复杂度为 \(O(n\sqrt n)\). 代码 #incl

NOI2016模拟赛Zbox loves stack

1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 using namespace std; 7 #define PI pair<int,int> 8 #define fi first 9 #define se second 10 #define mp(a,b) make_p

【BZOJ】【2741】【FOTILE模拟赛】L

可持久化Trie+分块 神题……Orz zyf & lyd 首先我们先将整个序列搞个前缀异或和,那么某一段的异或和,就变成了两个数的异或和,所以我们就将询问[某个区间中最大的区间异或和]改变成[某个区间中 max(两个数的异或和)] 要是我们能将所有[l,r]的答案都预处理出来,那么我们就可以O(1)回答了:然而我们并不能. 一个常见的折中方案:分块! 这里先假设我们实现了一个神奇的函数ask(l,r,x),可以帮我们求出[l,r]这个区间中的数,与x最大的异或值. 我们不预处理所有的左端点,我

bzoj2741: 【FOTILE模拟赛】L

2741: [FOTILE模拟赛]L Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 2679  Solved: 766[Submit][Status][Discuss] Description FOTILE得到了一个长为N的序列A,为了拯救地球,他希望知道某些区间内的最大的连续XOR和. 即对于一个询问,你需要求出max(Ai xor Ai+1 xor Ai+2 ... xor Aj),其中l<=i<=j<=r. 为了体现在线操作,对于一

noip模拟赛 街灯

分析:对于前30%的数据直接暴力模拟即可,对于另外30%的数据,因为每次的p是一样的,所以可以用莫队来维护,先离散化一下,再用一个桶统计次数. 100%的做法和之前做过的一道模拟赛题很像,当模数很小的时候分块,否则就暴力修改.其实看到区间操作第一感觉是线段树,但是线段树并不能维护这个,分块维护的信息多一些,所以分块.在模数较小的时候记录一下第i个块,模p等于v的有多少个,即g[i][p][v],利用前缀和统计1~i个块的个数.在模数较大的时候因为只有v,v+p,v+2p对答案有影响,所以记录第i

【前行】◇第3站◇ 国庆训练营&#183;OI制模拟赛

[第3站] 国庆训练营·OI制模拟赛Ⅰ 怀着冲刺提高组400的愿望来到这个very small but very interesting 的训练营QwQ 在北大dalao的带领下开始了第一场OI模拟赛[炸心态ヽ(*.>Д<)o゜] ? 简单总结 感觉非常爆炸…… 第一题还好,一眼看出结论题,所以开始打表……没想到只打出来了一种情况(为什么全是特殊情况),然后就凉了. 第二题就开始崩溃了.首先画图思考了大概20分钟……然后发现想不出正解,就开始想要骗分.看了看数据阶梯,发现自己好像只能做前1/3

2.23模拟赛

浪了一发普及模拟赛 题面见此 [样例输入] 4 1 1 2 1 1 1 [样例输出] 20 [数据规模和范围] 对于 30%的数据,n≤1000. 对于另外 30%的数据,bi=1. 对于 100%的数据,n≤1 000 000,bi≤1000. sol:单边记贡献,(x,y)边的贡献就是 Size[y]*(n-Size[y])*Dis[x][y],因为父亲都小于当前点,直接倒着跑一遍记Size就可以了,如果无序的话可以用拓扑 #include <bits/stdc++.h> using na

ZROI 19.08.07模拟赛

传送门 写在前面:为了保护正睿题目版权,这里不放题面,只写题解. "正睿从来没有保证,模拟赛的题目必须原创." "文案不是我写的,有问题找喵老师去."--蔡老师 A R爷再次翻车,搞出来了一道六年前的CF题. \(100pts:\) 然而不是原题也很简单,斜率优化板子,单调队列搞一下就完事了. 也可以wqs二分,复杂度可以做到\(O(m\log m)\),\(与\)p\(无关.所以R爷差点把\)p$出到\(10^5\). B 本题乱搞做法非常多,所以R爷动用了权限来

10.2模拟赛总结

10.2 模拟赛总结 T1. 数位dp: 一个非常非常非常非常显然的数位 DP \([L,R] = [1,R]-[1,L-1]\) 所以是分别求两次小于等于某个数字的方案数 \(f(i,j,k)\) 表示从低位数起的第 \(i\) 位,按照规则计算后答案为 \(j\quad (j=0,1)\) \(k\) 表示只考虑后面结尾和 \(lmt\)后面几位 的大小关系 \((k=0,1)\) 考虑第 \(i+1\) 位,算一下新构成的数字并判断下大小就可以了 注意到 \(L,R\) 数据范围特别大,需