P3709 大爷的字符串题(莫队+离散化)

P3709 大爷的字符串题

题目背景

在那遥远的西南有一所学校,

/被和谐部分/

然后去参加该省省选虐场,

然后某蒟蒻不会做,所以也出了一个字符串题:

题目描述

给你一个字符串 \(a\),每次询问一段区间的贡献。

贡献定义:

每次从这个区间中拿出一个字符 \(x\) ,然后把 \(x\) 从这个区间中删除,直到区间为空。你要维护一个集合 \(S\)

  • 如果 \(S\) 为空,你 \(rp\)\(1\)
  • 如果 \(S\) 中有一个元素不小于 \(x\),则你 \(rp\)\(1\),清空 \(S\)
  • 之后将 \(x\) 插入 \(S\)

由于你是大爷,平时做过的题考试都会考到,所以每次询问你搞完这段区间的字符之后最多还有多少 \(rp\)\(rp\) 初始为 \(0\)

询问之间不互相影响~

输入格式

第一行两个整数 \(n,m\),表示字符串长度与询问次数。

之后一行 \(n\) 个数,第 \(i\) 个整数表示给出的字符串的第 \(i\) 个字符 \(x_i\)

接下来 \(m\) 行,每行两个整数 \(l, r\),表示一次询问的区间。

输出格式

对于每次询问,输出一行一个整数表示答案。

输入输出样例

输入 #1

3 3
3 3 3
3 3
3 3
3 3

输出 #1

-1
-1
-1

说明/提示

数据规模与约定

  • 对于 \(10\%\) 的数据,是样例。
  • 对于另外 \(10\%\) 的数据,保证 \(n,m \le 100;\)
  • 对于另外 \(10\%\) 的数据,保证 \(n,m \le 10^3;\)
  • 对于另外 \(10\%\) 的数据,保证 \(n,m \le 10^4;\)
  • 对于另外 \(10\%\) 的数据,保证 \(n,m \le 10^5;\)
  • 对于 \(100\%\) 的数据,\(1 \leq n,m \le 2 \times10^5,1 \leq a_i \leq 10^9,1 \leq l, r \leq n。\)

保证数据像某省省选 day1T2 一样 sb,大家尽情用暴力水过题吧!

没事,你只要在一个好学校,就算这题只能拿到 10 分,也可以进队了。

Solution

唔,其实这是道语文题,题目要求
给定一个区间,求区间内众数的出现次数

为啥是这样,我们看这样一个样例

111122233345
12345/123/123/1

可以发现我们把序列排成一段一段的递增序列是最优的,每进入新的一段,\(rp\)就会-1
段落数也就是求出现次数最多的数的出现次数,因为后面段落出现的数一定也在前面的段落中出现过

用莫队求解
我们用\(cnt[x]\)表示\(x\)的出现次数,很明显在\(add()\)\(ans=max(ans,cnt[x])\)

但是删除的时候,可能会遇到这样一个问题,当前的\(cnt[x]\)恰好就等于\(ans\),那我们能直接使\(ans--\)吗?不能!因为可能还有其他的\(cnt[y]\)也等于\(ans\),删掉一个\(x\)不足以改变我们的答案

所以现在还要开一个数组\(p[cnt]\),表示出现次数为\(cnt\)的数字有多少个
\(remove()\)函数中,如果当前的\(cnt[x]==ans\)并且\(p[cnt[x]]==1\),我们才能使ans--

嗷,有一个细节,这道题的值域范围是\(10^9\),需要离散

Code

#include<bits/stdc++.h>
#define il inline
#define rg register
#define lol long long
#define in(i) (i=read())
using namespace std;

const  int N=2e5+5;

int read() {
    int ans=0,f=1; char i=getchar();
    while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
    while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+(i^48),i=getchar();
    return ans*f;
}

int n,m,k,block,ans;
int a[N],sum[N],cnt[N],p[N],b[N];

struct query{
	int l,r,id,pos;
	bool operator < (const query &a) const {
		return pos==a.pos?r<a.r:pos<a.pos;
	}
}t[N];

void add(int x) {
	p[cnt[a[x]]]--;
	cnt[a[x]]++;
	p[cnt[a[x]]]++;
	ans=max(ans,cnt[a[x]]);
}

void remove(int x) {
	if(p[cnt[a[x]]]==1 && ans==cnt[a[x]]) ans--;
	p[cnt[a[x]]]--;
	cnt[a[x]]--;
	p[cnt[a[x]]]++;
}

int main() {
	in(n), in(m); block=3*sqrt(n);
	for (rg int i=1;i<=n;i++) in(a[i]),b[i]=a[i];
	sort(b+1,b+1+n); int len=unique(b+1,b+1+n)-b-1;
	for (int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+len,a[i])-b;
	for (rg int i=1,l,r;i<=m;i++)  {
		in(l), in(r);
		t[i].l=l, t[i].r=r;
		t[i].id=i;
		t[i].pos=(l-1)/block+1;
	}
	sort(t+1,t+1+m);
	for (rg int i=1,curl=1,curr=0;i<=m;i++) {
		rg int l=t[i].l,r=t[i].r;
		while(curl<l) remove(curl++);
		while(curl>l) add(--curl);
		while(curr<r) add(++curr);
		while(curr>r) remove(curr--);
		sum[t[i].id]=-ans;
	}
	for (rg int i=1;i<=m;i++) cout<<sum[i]<<endl;
}
posted @ 2021-08-22 10:58  real_l  阅读(49)  评论(0编辑  收藏  举报