luogu P3709 大爷的字符串题

5 篇文章 0 订阅

题面传送门
看上去很玄学的亚子。
我们很容易发现,要想 r p rp rp最大,要让每次取出的都是一个严格上升的序列,则原问题转换成最少取几次。
那么对于有 v i v_i vi的一个数,一定要取 v i v_i vi次才能取完。则答案为区间内最多的数。则变成了求区间众数。
那么可以用莫队求区间众数,开一个 f i f_i fi数组记录 i i i这个数有几个, s i s_i si记录有 i i i个的数有几个,用二重辅助来求解。
代码实现:

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int n,m,k,ans[200039],l,r,f[200039],sf[200039],h[200039],a[200039],head;
struct yyy {
	int x,y,num;
} fs[200039],s[200039];
inline void read(register int &x) {
	x=0;register char s=getchar();
	while(s<'0'||s>'9')s=getchar();
	while(s>='0'&&s<='9')x=(x<<3)+(x<<1)+(s^48),s=getchar();
}
inline void print(register int x) {
	if(x<0) {putchar('-');print(-x);return;}
	if(x>9) print(x/10);
	putchar(x%10+'0');
}
inline bool cmp1(yyy x,yyy y) {return x.x<y.x;}
inline bool cmp2(yyy x,yyy y) {return (h[x.x]==h[y.x])?((h[x.x]&1)?x.y<y.y:x.y>y.y):(x.x<y.x);}
int main() {
	register int i,l,r,head=0;
	scanf("%d%d",&n,&m);
	k=sqrt(n);
	for(i=1; i<k; i++) h[i]=1;
	for(i=k; i<=n; i++) h[i]=h[i-k]+1;
	for(i=1; i<=n; i++)read(a[i]),fs[i].x=a[i],fs[i].num=i;
	sort(fs+1,fs+n+1,cmp1);
	a[fs[1].num]=1;
	for(i=2; i<=n; i++) {
		if(fs[i].x==fs[i-1].x) a[fs[i].num]=a[fs[i-1].num];
		else a[fs[i].num]=a[fs[i-1].num]+1;
	}
	for(i=1; i<=m; i++) read(s[i].x),read(s[i].y),s[i].num=i;
	sort(s+1,s+m+1,cmp2);
	l=1;r=0;
	sf[0]=1e9;
	for(i=1; i<=m; i++) {
		while(l<s[i].x) {sf[--f[a[l]]]++;sf[f[a[l++]]+1]--;while(!sf[head])head--;}
		while(r>s[i].y) {sf[--f[a[r]]]++;sf[f[a[r--]]+1]--;while(!sf[head])head--;}
		while(l>s[i].x) {sf[++f[a[--l]]]++;sf[f[a[l]]-1]--;while(sf[head+1])head++;}
		while(r<s[i].y) {sf[++f[a[++r]]]++;sf[f[a[r]]-1]--;while(sf[head+1])head++;}
		ans[s[i].num]=-head;
	}
	for(i=1; i<=m; i++) print(ans[i]),putchar('\n');
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值