首页 > 其他分享 >【Ynoi 2019 模拟赛】Yuno loves sqrt technology III

【Ynoi 2019 模拟赛】Yuno loves sqrt technology III

时间:2024-09-07 19:36:36浏览次数:5  
标签:cnt const int Ynoi sqrt 众数 区间 III

Luogu P5048 Yuno loves sqrt technology III

题意

给定一个长度为 \(n\) 的序列 \(a\)。

有 \(m\) 次询问:查询区间 \([l,r]\) 中众数的出现次数。

强制在线。

数据范围与约定

\(1 \le n,m,a_i \le 5*10^5\)。

题解

十年前《蒲公英》的做法,这道题只能拿 \(80\) 分,因为这道题卡了空间。下面来讲一个现代的在线区间众数做法。

看到区间查询,首先还是想到线段树。我们发现这题线段树没办法 pushup。但是与《由乃打扑克》这题不同的是,这题不能 pushup 的原因是我们不会 merge 两个区间的信息,而不是因为层数。所以乍一看分块也无能为力,想要莫队却被强制在线 ban 了。

如果可以像区间数颜色一样,通过某种方式转化成二维数点,或者类似于区间 mex 进行一些转化,也许能做。但遗憾的是区间众数起码我所知道的,并不能进行转化。

还是考虑分块,不考虑区间信息合并的情况下,看看该怎么办。分类讨论一下:

  • 查询区间在一个块内:开个桶计数,暴力扫一遍每个元素即可。特别强调不要用 memset 清空桶,复杂度不对。

  • 查询区间在多个块内:这里也分两种情况,整块和角块:

    • 整块:既然不会信息的 merge,那我们就不 merge 了。直接预处理出任意两个块之间的众数。因为块只有 \(O(\sqrt{n})\) 个,所以只会有 \(O(n)\) 个区间,如同《蒲公英》的预处理一样。假设整块合并后,众数为 \(mode\),出现次数为 \(cnt\)。

    • 角块:如果我们已经有了中间整块的众数信息了,那么考虑到角块最多有 \(2*\sqrt{n}\) 的数,考虑角块上的数的影响。如果我们能获取一个数在区间中出现的次数,就能更新 \(mode,cnt\) 了。但遗憾的是,这个做法就是已经过时的《蒲公英》的做法,需要开一个 \(n*\sqrt{n}\) 大小的前缀和数组,被卡空间了。

      如果我们转成判定问题呢?考虑到,\(cnt\) 最多加上 \(O(\sqrt{n})\) 次,总势能是固定的。可以进行判定,一个数如果在区间 \([l,r]\) 中出现次数大于 \(cnt\),就可以给 \(cnt+1\),然后进行 while 循环。剩下的问题就是如何判定了。

      我们可以开一个 vector 数组,记录每种数字出现的下标。然后我们再记录一下每个数在对应的 vector 中的下标。对于左角块的每个数,如果在 vector 中的下标,向右偏移 \(cnt\) 次后,下标小于等于 \(r\)。则说明在区间 \([l,r]\) 中至少出现了 \(cnt+1\) 次。右角块的数同理。

最后我们就得到了时间复杂度 \(O(n\sqrt{n})\),空间复杂度 \(O(n)\) 的做法了。最后根据实测,块长取 \(400\) 时跑得最快。

代码

#include<bits/stdc++.h>
using namespace std;
/*====================*/
#define endl "\n"
/*====================*/
typedef long long lnt;
/*====================*/
const int N = 5e5 + 10;
const int C = 5e5 + 10;
/*====================*/
const int S = 400;
const int B = N / S + 10;
/*====================*/
int n, m;
int col[N];
/*====================*/
int belong[N];
struct Block
{
	int l, r;
	Block(void)
	{
		l = r = 0;
	}
}block[B];
int mode[B][B];
/*====================*/
int idxincolidx[N];
vector<int>colidx[C];
/*====================*/
int cnt[C];
/*====================*/
int Ask(int l, int r)
{
	int res = 0;
	if (belong[l] == belong[r])
	{
		for (int i = l; i <= r; ++i)
		{
			res = max(res, ++cnt[col[i]]);
		}
		for (int i = l; i <= r; ++i)
		{
			cnt[col[i]]--;
		}
	}
	else
	{
		res = mode[belong[l] + 1][belong[r] - 1];
		for (int i = l; i <= block[belong[l]].r; ++i)
		{
			while ((idxincolidx[i] + res < colidx[col[i]].size()) && (colidx[col[i]][idxincolidx[i] + res] <= r))
			{
				res++;
			}
		}
		for (int i = block[belong[r]].l; i <= r; ++i)
		{
			while ((idxincolidx[i] - res >= 0) && (colidx[col[i]][idxincolidx[i] - res] >= l))
			{
				res++;
			}
		}
	}
	return res;
}
/*====================*/
void Solve(void)
{
	do
	{
		cin >> n >> m;
		for (int i = 1; i <= n; ++i)
		{
			cin >> col[i];
		}
	} while (false);//读入

	do
	{
		for (int i = 1; i <= n; ++i)
		{
			colidx[col[i]].push_back(i);
			idxincolidx[i] = colidx[col[i]].size() - 1;
		}
	} while (false);//预处理各颜色下标

	do
	{
		for (int i = 1; i <= n; ++i)
		{
			belong[i] = i / S + 1;
			if (block[belong[i]].l == 0)
			{
				block[belong[i]].l = i;
			}
			block[belong[i]].r = i;
		}
	} while (false);//分块预处理

	do
	{
		for (int i = 1; i <= belong[n]; ++i)
		{
			for (int j = i; j <= belong[n]; ++j)
			{
				mode[i][j] = mode[i][j - 1];
				for (int k = block[j].l; k <= block[j].r; ++k)
				{
					mode[i][j] = max(mode[i][j], ++cnt[col[k]]);
				}
			}
			memset(cnt, 0, sizeof(cnt));
		}
	} while (false);//处理块间众数

	do
	{
		int lastans = 0;
		for (int i = 1; i <= m; ++i)
		{
			int l, r; cin >> l >> r;
			l ^= lastans; r ^= lastans;
			cout << (lastans = Ask(l, r)) << endl;
		}
	} while (false);//回答询问
}
/*====================*/
int main()
{
#ifndef ONLINE_JUDGE
	freopen("IN.txt", "r+", stdin);
#endif
	ios::sync_with_stdio(false);
	cin.tie(NULL), cout.tie(NULL);
	int T = 1; //cin >> T;
	while (T--)Solve();
	return 0;
}

标签:cnt,const,int,Ynoi,sqrt,众数,区间,III
From: https://www.cnblogs.com/ProtectEMmm/p/18402055

相关文章

  • 【Ynoi 2016】掉进兔子洞
    LuoguP4688掉进兔子洞题意给定一个长度为\(n\)的序列\(a\)。有\(m\)次询问:每次询问给定三个区间,问将三个区间内同时出现的数删掉后,还剩下多少个数。每次询问独立。数据范围与约定\(1\len,m\le10^5\),\(1\lea_i\le10^9\)。题解首先发现,每次询问的答案形式为:\(......
  • P10878 [JRKSJ R9] 在相思树下 III 题解
    Description给定一个长为\(n\)的序列\(a_{1\dotsn}\),需要对它进行两种操作共\(n-1\)次。对一个长度为\(l\)的序列\(b_{1\dotsl}\)进行一次操作将会把序列变为一个长为\(l-1\)的序列\(c_{1\dotsl-1}\):操作一中,\(\foralli\in[1,l),c_i=\max(b_i,b_{i+1})\);操作......
  • 题解:洛谷 P10878 [JRKSJ R9] 在相思树下 III
    原题链接解析在操作一时,最小值如果在最后一位,其无法更新任何数,会被删除;否则不在最后一位时一定会被其右侧更大的数更新。所以在操作一时,最小值一定会被更新掉。同理,在操作二时,最大值一定会被更新掉。由此,操作一决定了答案的下限,操作二决定了答案的上限。所以可以得出贪心策略......
  • 【dp力扣】买卖股票的最佳时机III
    目录审题通过动态规划固定套路思考:1、定义状态表示(关键)2、推导状态转移方程(重点)对于buy(可买入股票):回顾状态表示:第一种情况:第二种情况:联立两种情况(取两种情况的最大值):​对于own(持有股票)回顾状态表示:第一种情况:第二种情况:(最终结果)联立两种情况(还是取max):3、初......
  • Ynoi 做题笔记(2024 年暑假)
    P9992[YnoiEasyRound2024]TEST_130之前大概想出来了,但是没想清楚。发现每次询问\(w,d\)就相当于算\(w\)子树里离\(w\)距离不超过\(d\)的点的贡献之和,\(w\)的贡献是\(d+1\)(因为\(N(w,0),N(w,1),\ldots,N(w,d)\)都可以),\(w\)往下第一层的每个点分别的贡......
  • mac游戏:魔兽争霸3冰封王座Warcraft III for mac 版
    游戏背景设定在魔兽世界的广阔舞台上,玩家将参与到一场场史诗般的战役中。在《冰封王座》中,新增了两个大系列的剧情战役,进一步丰富了游戏的故事情节。这些战役围绕着伊利丹·怒风、阿尔萨斯等经典角色展开,讲述了他们在燃烧军团入侵、巫妖王力量复苏等关键事件中的冒险与斗争。......
  • 深入理解 UCOSIII 软件定时器
    一、引言在嵌入式系统开发中,定时器是一种非常重要的工具。UCOSIII作为一款广泛应用的实时操作系统,其软件定时器功能为开发者提供了强大的定时解决方案。本文将深入探讨UCOSIII软件定时器的工作原理、使用方法以及实际应用中的注意事项。二、UCOSIII软件定时器概述UCOS......
  • [Ynoi2011] 初始化
    题目链接:[Ynoi2011]初始化神仙trick+卡常题,前缀后缀和根本没听过。根号分治+分块。对于修改操作,发现是跳着修改,考虑根号分治。若\(x\ge\sqrt{n}\),直接暴力更改,复杂度\(O(\sqrt{n})\)。反之,可以将序列抽象成一堆大小为\(x\)的段,如图,\(l,r\)是查询的区间。发现\(......
  • LeetCode-Python-1650. 二叉树的最近公共祖先 III
    给定一棵二叉树中的两个节点 p 和 q,返回它们的最近公共祖先节点(LCA)。每个节点都包含其父节点的引用(指针)。Node 的定义如下:classNode{publicintval;publicNodeleft;publicNoderight;publicNodeparent;}根据维基百科中对最近公共祖先节点......
  • [Ynoi2017] 由乃的玉米田
    题目链接:[Ynoi2017]由乃的玉米田弱化版:小清新人渣的本愿这两道题都是看会不会用bitset,bitset大胜利减操作:用一个bitset\(vis1\)记录一个数是否出现,如果有就是\((vis1\And(vis>>x)).count()\ge1\),其实就是看是否有数\(a\)和数\(a+x\)同时出现加操作:同减操......