[洛谷3709]大爷的字符串题(区间众数)

题目描述

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

贡献定义:

每次从这个区间中随机拿出一个字符x,然后把x从这个区间中删除,你要维护一个集合S

如果S为空,你rp减1

如果S中有一个元素不小于x,则你rp减1,清空S

之后将x插入S

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

询问之间不互相影响~

输入输出格式

输入格式:

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

之后一行n个数,表示字符串

由于你是大爷,所以字符集1e9

之后m行每行两个数,表示询问的左右区间

输出格式:

m行,每行一个数表示答案

输入输出样例

输入样例#1: 
3 3
3 3 3
3 3
3 3
3 3
输出样例#1: 
-1
-1
-1

说明

前4个点1s,后面的点4s

对于10%的数据,是样例

对于另外10%的数据,n,m <= 100

对于另外10%的数据,n,m <= 1000

对于另外10%的数据,n,m <= 10000

对于另外10%的数据,n,m <= 100000

对于100%的数据,n,m <= 200000

题意很难懂啊

题意:求严格上升子序列的最少个数,每个数只能在一个子序列中且每个数的顺序可以自由交换,答案为个数*-1(即1 3 3 2的答案是-2),然后给出几个询问,每次询问一个区间,求最少个数。

我们发现严格上升子序列的最少个数取决于相同数字的出现个数。即严格上升子序列的最少个数为询问的区间内的众数的出现次数

莫队

由于数字最大可以是1e9,所以我们要先离散化

我们用cnt[i]表示数i的出现次数,用num[i]表示出现i次的数字个数,sum表示出现次数最多的数的出现次数(即众数的出现次数)

对于把区间向左,向右扩展一个数,我们判断扩展那个数的出现次数是不是当前区间内众数的出现次数(cnt[i]==sum),是则更新众数的出现次数(sum++),然后更新num,cnt什么的

对于把区间最左边(右边)删除一个数,我们判断删除那个数的出现次数是不是当前区间内众数的出现次数(cnt[i]==sum),是则再判断这个出现次数是不是只有这一个数(num[i]==1),若是,则意味着区间众数的出现次数变少(sum--),然后更新num,cnt什么的

num[0]的初始值要设成数字的种类数(即出现了多少种的不同数字 1 3 3 2 num[0]=3),我们可以理解为初始[l,r]区间内,有这么多个数出现了0次

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
struct xxx{
    int l,r,block,id;
}q[200001];
struct xxx2{
    int val,id;
}a[200001];
int b[200001],cnt[200001],num[200001],ans[200001];
bool cmp(xxx a,xxx b){if(a.block!=b.block)return a.block<b.block;return a.r<b.r;}
bool cmp2(xxx2 a,xxx2 b){return a.val<b.val;}
int main()
{
    int n,m;scanf("%d%d",&n,&m);int T=(int)sqrt((double)n);
    for(int i=1;i<=n;i++){scanf("%d",&a[i].val);a[i].id=i;}
    sort(a+1,a+n+1,cmp2);int last=0;
    for(int i=1;i<=n;i++)
    {
        if(a[i].val!=a[i-1].val)last++;
        b[a[i].id]=last;
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&q[i].l,&q[i].r);q[i].id=i;q[i].block=(q[i].l+1)/T;
    }
    sort(q+1,q+m+1,cmp);
    int l=1,r=0,sum=0;num[0]=last;
    for(int i=1;i<=m;i++)
    {
        while(r<q[i].r){r++;if(cnt[b[r]]==sum)sum++;num[cnt[b[r]]]--;cnt[b[r]]++;num[cnt[b[r]]]++;}
        while(r>q[i].r){if(cnt[b[r]]==sum&&num[cnt[b[r]]]==1)sum--;num[cnt[b[r]]]--;cnt[b[r]]--;num[cnt[b[r]]]++;r--;}
        while(l<q[i].l){if(cnt[b[l]]==sum&&num[cnt[b[l]]]==1)sum--;num[cnt[b[l]]]--;cnt[b[l]]--;num[cnt[b[l]]]++;l++;}
        while(l>q[i].l){l--;if(cnt[b[l]]==sum)sum++;num[cnt[b[l]]]--;cnt[b[l]]++;num[cnt[b[l]]]++;}
        ans[q[i].id]=sum;
    }
    for(int i=1;i<=m;i++)printf("%d\n",-ans[i]);
    return 0;
} 

 

posted @ 2017-11-13 22:40  lher  阅读(779)  评论(0编辑  收藏  举报