loj10121 与众不同(ST算法)(二分)

xiaoxiao2025-06-10  29

题目

A 是某公司的 CEO,每个月都会有员工把公司的盈利数据送给 A,A 是个与众不同的怪人,A 不注重盈利还是亏本,而是喜欢研究「完美序列」:一段连续的序列满足序列中的数互不相同。 A 想知道区间 [L,R] 之间最长的完美序列长度。

尝试

一开始想的是用权值线段树,这样只要满足min[l,r]=1那么这段就是合法的。 又想了想让每个合法区间对提问更新,并不太可行。

题解

ST表+二分 绕了一大圈,其实应该直接求以i为结尾的最大长度... 设其为f[i],为了方便递推,再设一个st[i]表示以i结尾的最大长度的开头,那么有。 st[i]的转移有两条限制:一是显然不能比st[i-1]还前(小),二是不能到最近一次出现的a[i](a[i]为盈利价值)。用last[i]维护数字i最近的位置,所以。

考虑求答案吧,一段区间[ql,qr],那么最优解的右端点一定在[ql,qr]之间。左端点呢,随意对吧,没有什么要求。但还是有一点特别的,对于一个k,如果st[k]<ql而且st[k-1]<ql,那么k一定比k-1优秀。 所以答案的来源只有两种可能了,一是一个最大的k满足st[k]<ql,二是从k+1(根据上面k的特点,有st[k+1]>=ql)开始一直到r,f的最大值,即。 那么对于那段区间最大值,用ST表维护就好了。k的值因为st递增,所以二分可以得到。 还要注意一下k的细节,如果st[r]<l要特判一下,此时k会超出[l,r]的范围。

代码

#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN=2e5+10,MAXA=1e6; int bin[20],log[MAXN]; void pre_work() {     for(int i=0;i<=20;i++) bin[i]=1<<i;     log[0]=-1;for(int i=1;i<MAXN;i++) log[i]=log[i>>1]+1; } int n,m; int st[MAXN]; int last[MAXA*2+10]; int f[MAXN][20]; int query(int l,int r) {     int s=log[r-l+1];     return max(f[l][s],f[r-bin[s]+1][s]); } int main() {     pre_work();     scanf("%d%d",&n,&m);     for(int i=1;i<=n;i++)     {         int x;scanf("%d",&x);x+=MAXA;         st[i]=max(st[i-1],last[x]+1);         f[i][0]=i-st[i]+1;//debug f[i][0]=st[i]         last[x]=i;     }     for(int i=1;i<=19;i++)         for(int j=1;j+bin[i]-1<=n;j++) f[j][i]=max(f[j][i-1],f[j+bin[i-1]][i-1]);//debug j<=n     while(m--)     {         int l,r,k;         scanf("%d%d",&l,&r);l++,r++;         if(st[r]<l) k=r+1;         else k=lower_bound(st+1,st+n+1,l)-st;         int ans=0;         if(k>l) ans=k-l;         if(k<=r) ans=max(ans,query(k,r));         printf("%d\n",ans);     }     return 0; }

 

转载请注明原文地址: https://www.6miu.com/read-5031623.html

最新回复(0)