首页 > 其他分享 >洛谷P3498 [POI2010] KOR-Beads 题解

洛谷P3498 [POI2010] KOR-Beads 题解

时间:2024-03-23 20:34:00浏览次数:24  
标签:Beads 洛谷 int 题解 MAXN ull s2 hash const

P3498 [POI2010] KOR-Beads

对于数列 ${A_i}$ ,求 $k$ 使数列被分为 $\lfloor \frac{n}{k}\rfloor$ 个部分后不同子数列种类最多(子串翻转前后为一类子串)

  • 很容易想到:枚举 $k\ \in\ [1,n]$ ,记录每个 $k$ 下不同种类数,然后取最优即可,那么问题来了:如何计算种类数?

    • 暴戾算法:

      一种纯粹的暴戾算法(雾)便是:对于每个 $k$ 使用 mapstring 记录当前串是否出现过,但问题在于字符串需要不断改变,即需要 $2e5$ 的 string 变量,无论是时间复杂度还是空间复杂度都是不可接受的。

    • 正解哈希:

      因此我们直接使用 hash 算法,并选择 map 或者 set 判重。

      /*code by CheemsaDoge*/
      #include <bits/stdc++.h>//用了万能头,我就是啸纣老师口中的低水平(哭
      template<typename T> inline void read(T &x) {/*...*/}//fast input
      template<typename T> inline void write(T x){/*...*/}
      const int MAXM=1e6+1145;const int MAXN=200000+11145;const int INF=2147483647ll;//2^31-1
      #define pc putchar('\n')
      #define pk putchar(' ')
      #define ull unsigned long long
      /*---------------------------------pre------------------------------------*/
      std::set<ull>hash;//直接用unsigned long long让他自己溢出来
      const ull sp=19260817;
      ull s1[MAXN],s2[MAXN],pp[MAXN];
      int a[MAXN],n;
      ull get1(int l,int r) {return s1[r]-s1[l-1]*pp[r-l+1];} //获得hash值(从前往后计算
      ull get2(int l,int r) {return s2[l]-s2[r+1]*pp[r-l+1];} //获得hash值(从后往前计算
      template <typename T> T min(const T _a,const T _b) {return _a<_b?_a:_b;} //手写 min 函数
      int get_ans(int k) {
      	hash.clear();
      	for(int i=0;i*k+k<=n;i++) hash.insert(min<ull>(get1(i*k+1,i*k+k),get2(i*k+1,i*k+k))); //插入当前字符串(子串)的hash值
      	return hash.size(); //返回种类数(set不允许出现相同元素,所以前面可以安心插
      }//得到答案
      std::vector<int>ans;//动态数组存储满足条件的k
      int maxn=-1;//最大的种类数
      int main(){
      	read(n);
      	for(int i=1;i<=n;i++) read(a[i]);
      	pp[0]=1;
      	for(int i=1;i<=n;i++) {
      		s1[i]=1ull*a[i]+s1[i-1]*sp;
      		pp[i]=pp[i-1]*sp;
      	}
      	for(int i=n;i>=1;i--) s2[i]=a[i]+s2[i+1]*sp;//初始化hash
      	for(int i=1;i<=n;i++) {
      		int ma=get_ans(i);//获得k的种类数
      		if(maxn<ma) { //更新答案
      			ans.clear();
      			ans.push_back(i);
      			maxn=ma;
      		}
      		else if(maxn==ma) ans.push_back(i);
      	}
      	write(maxn);pk;write(ans.size());pc;//输出(pc是putchar('\n'),前面的宏有定义
      	for(int i=0;i<(int)ans.size();i++) write(ans[i]),pk;//pk是putchar(' ')
      	return 0; //好习惯捏
      }
      

    关于模数:这道题如果 sp 被设为 10007 亲测会挂,机房里的 $dalao$ 就有这么挂的。

    所以一个好的模数是必不可少的,建议用 13331 或者某人的生日 19260817

标签:Beads,洛谷,int,题解,MAXN,ull,s2,hash,const
From: https://www.cnblogs.com/cheemsadoge/p/18091635

相关文章

  • 跳马【华为OD机试JAVA&Python&C++&JS题解】
    一.题目马是象棋(包括中国象棋和国际象棋)中的棋子,走法是每步直一格再斜一格,即先横着或直着走一格,然后再斜着走一个对角线,可进可退,可越过河界,俗称“马走‘日’字。给顶m行n列的棋盘(网格图),棋盘上只有有棋子象棋中的棋子“马”,并且每个棋子有等级之分,等级为k的马可以跳1~k......
  • CF710D Two Arithmetic Progressions 题解
    CF710DTwoArithmeticProgressions根号分治薄纱数论看日报学习的根号分治:暴力美学——浅谈根号分治-paulzrm的博客。开始想学ODT的映射思想的推广-金珂拉的博客,结果先学了ODT,又学了根号分治,才搞懂前置知识。什么是根号分治根号分治,是暴力美学的集大成体现。与......
  • [暴力题解系列]2023年蓝桥杯-子串简写
    ​ 大伙都说暴力是最低级的算法,啊那确实。但是好的暴力才是真正牛逼的骗分。咱就是说,暴力如何骗分呢?就是基于最暴力的算法一步步优化到能得更多分的暴力。子串简写这题,首先第一步就能想到一件事情:暴力枚举子串开头和末尾的位置,检查是否是符合题目要求的字符,如果是,并且长度大于......
  • C语言:洛谷题目分享(4)小书童--凯撒密码和笨小猴
    目录1.前言2.俩道题目1.小书童--凯撒密码1.题目背景2.题目描述3.输入格式4.输出格式5.题解2.笨小猴1.题目描述2.输入格式3.输出格式4.题解3.小结1.前言哈喽大家好啊,今天我继续为大家分享洛谷题单的俩道题目,请大家多多支持喔~2.俩道题目1.小书童--凯撒密码......
  • 题解:AT_arc174_a [ARC174A] A Multiply
    题传。先要将\(C\)分类。\(C>0\),为了使答案更大,要乘上一个最大的区间和。\(C\le0\),为了使答案更大,选择乘上一个最小的区间和,因为此时我们可以贪心地想,如果区间和越小,乘上一个负数或\(0\)后,答案减少得越小,甚至乘上负数,还会使答案增大,所以也可以用负负得正来解释。当......
  • P8756 [蓝桥杯 2021 省 AB2] 国际象棋 题解
    设计状态什么的就不讲了,这里是对其它题解的优化。怎么优化呢,我们可以知道的是我们只要明确了当前行的状态,上一行的可选集就是知道的,如果我们明确了当前行以及上一行的状态,那么上上行的可选集就是知道的,于是我们就可以使用二进制子集枚举来写,这样就减去了全部不合法的枝叶,我们可以......
  • CF794F Leha and security system 题解
    题目链接:CF或者洛谷首先观察到题目的修改\(x\rightarrowy\),是每个位置的\(x\)都要变,那就显然的拆位去算每一位的贡献。当然,你又发现\(x\rightarrowy\),这玩意属于值为\(x\)的位变化成\(y\),那么这个和普通的拆位区别就在于这是维护值域维的拆位,我们拆位\(0\sim9\)......
  • [HDU5396] Expression 题解
    每次合并两个数,做过石子合并的人都能看出来是区间dp。设状态\(dp_{i,j}\)表示区间\([i,j]\)中合并为一个数的所有情况之和。那么我们就可以枚举断点\(k\):\(b_k\)为\(+\):\([i,k]\)中的每种情况都要和\([k+1,j]\)中的每种情况产生一个贡献,所以总贡献为\(dp_{i,k}\ti......
  • 2024年3月22号题解
    Fliptile解题思路对于这个题目可以用递推来写因为每次翻转只会影响一个十字架的区域,所以如果我们知道第一行的情况,那么是不是只要把第一行的所有的1在对应的下一个行的相同位置进行翻转就可以把第一行的所有的1变成0呢(重要性质) 那么只要我们不断递推下去就可以得到最后一......
  • CF1946E 题解
    Blog赛场上差一点做出来。首先发现左右两部分是比较独立的,所以可以分开计算后合并。注意到我们可以把整个数集分成左右两部分,即\(\binom{n-1}{p_{m1}-1}\)。然后我们不妨只考虑左边。发现左边的最大值也已经确定,且最大值右边的所有数可以随便选,即\(\binom{p_{i+1}-......