虽然T2 做法假了 不过稍微改一下 就可以写luogu另外一道类似的题目
多次查询一个区间内 相同数字 最远的距离
那么 我们再考虑 查询区间内 相同数字 最近的距离
看似特别相同的两个问题 我们思考一下 是否可以使用同一种方法写过
确实 莫队和分块 都可以写过 但是 我确实是 想到了线段树的做法
那么第一问是今天模拟赛的题目 我确实写了个一个线段树 很快就写完了 过了大小样例 就没再管
下考场之后 才知道 样例的数据确实很水 让我很巧的避免了所有 我代码中错误的地方
那么不妨我先来分析 第二问的做法 可以借鉴 CF522D Closest Equals 这个题目
此时 查询最小距离 显然我们可以直接 维护离他最近的距离即可
因为 最小距离 具有最优性 考虑此时只维护左边第一个和他相同的数字出现的位置
对于 询问我们不妨将询问离线 按照右端点从小到大排序 维护一下区间最小值即可 看code吧
//查询一个区间内 相同的数字的之间的最小距离 //首先将 询问离线 按右端点排序 对于每一个x 我们只关心 离他最近的x 那么维护左边第一个x出现的位置 //线段树维护区间最小值即可 #includeusing namespace std; template inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } const int maxn=510000; const int inf=1e9; int n,m,x,q,ans[maxn],last[maxn],p[maxn]; map<int,int>mp; struct node { int l,r,id; }s[maxn]; int cmp(node x,node y){return x.r<y.r;} struct Tree { int l,r,minn; }t[4*maxn]; void build(int p,int L,int R) { t[p].l=L;t[p].r=R;t[p].minn=inf; if(L>=R)return ; int mid=(t[p].l+t[p].r)>>1; build(2*p,L,mid); build(2*p+1,mid+1,R); } inline void update(int p,int pos,int num) { if(t[p].l==t[p].r&&t[p].l==pos){t[p].minn=min(t[p].minn,num);return ;} int mid=(t[p].l+t[p].r)>>1; if(pos<=mid)update(2*p,pos,num); else update(2*p+1,pos,num); t[p].minn=min(t[2*p].minn,t[2*p+1].minn); } inline int query(int p,int L,int R) { if(L<=t[p].l&&R>=t[p].r) return t[p].minn; int mid=(t[p].l+t[p].r)>>1; int res=inf; if(L<=mid)res=min(res,query(2*p,L,R)); if(R>mid)res=min(res,query(2*p+1,L,R)); return res; } int main() { // freopen("1.in","r",stdin); // freopen("far.in","r",stdin); // freopen("far.out","w",stdout); read(n); read(q); build(1,1,n); for(int i=1;i<=n;i++) { read(x); if(mp[x]) p[i]=mp[x],mp[x]=i; else mp[x]=i,p[i]=0; } for(int i=1;i<=q;i++) { read(s[i].l); read(s[i].r); s[i].id=i; } sort(s+1,s+q+1,cmp); int r=1; for(int i=1;i<=q;i++) { while(r<=s[i].r) { if(p[r]) update(1,p[r],r-p[r]); r++; } int res=query(1,s[i].l,r-1); if(res>=inf) ans[s[i].id]=-1; else ans[s[i].id]=res; } for(int i=1;i<=q;i++) printf("%d\n",ans[i]); return 0; }
或许 你会认为第一问和第二问不是一样的吗 考虑此时我们仅仅维护 相邻答案一定是错的
那么考虑维护什么呢 维护一个离他最远的点 你不难想到 会直接考虑 维护第一次出现的位置
但是显然 我们会出现很多问题 不过我确实考场上是这么做的 不过线段树再去订正这个东西实在是太困难了
考虑 为什么错了 对于一组数据 现在在查询区间内 存在两个相同的数字 但是区间左端点之外依旧 存在一个点 并且是第一次出现的
那么 此时我的答案是0 虽然这种做法会过滤到不合法的情况 但是 我们不难想到 会遗漏很多答案 所以做法存在缺陷。
不过 回滚莫队和分块确实可以解决这个问题呢。 今天下午订正8
我的分块 会T 但是我也不会敲 回滚莫队qwq 所以考虑 丢一个90分的代码8
#includeusing namespace std; template inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } const int N=100010; int block,l,r,belong[N],n,m,q,num,tim[N],times; int a[N],L[N],R[N],first[N][350],pos[N],last[N][350],Ans[350][350]; inline int calc(int s,int t) { int ans=0; for(int i=L[s];i<=R[s];i++) { ans=max(ans,last[a[i]][t]-i); } return ans; } inline void build() { block=(int)sqrt(n+0.5);//块长 num=n/block;//块的大小 if(n%block) num++; for(int i=1;i<=num;i++) { L[i]=(i-1)*block+1; R[i]=i*block; } R[num]=n; for(int i=1;i<=n;i++) { belong[i]=((i-1)/block)+1; } memset(Ans,0,sizeof(Ans)); memset(first,0,sizeof(first)); memset(last,0,sizeof(last));times=0; for(int i=1;i<=n;i++) last[a[i]][belong[i]]=i; for(int i=n;i>0;i--) first[a[i]][belong[i]]=i; for(int i=1;i<=m;i++) { for(int j=1;j<=num;j++) { if(!last[i][j])last[i][j]=last[i][j-1]; } for(int j=num;j;j--) { if(!first[i][j]) first[i][j]=first[i][j+1]; } } for(int i=num;i;i--) { for(int j=i;j<=num;j++) { Ans[i][j]=max(max(Ans[i+1][j],Ans[i][j-1]),calc(i,j)); } } } inline int query(int s,int t) { ++times; int ans=0; if(belong[s]==belong[t]) { for(int i=s;i<=t;i++) { if(tim[a[i]]!=times) {tim[a[i]]=times;pos[a[i]]=i;} else {ans=max(ans,i-pos[a[i]]);} } return ans; } for(int i=s;i<=R[belong[s]];i++) { if(tim[a[i]]!=times) {tim[a[i]]=times;pos[a[i]]=i;} else {ans=max(ans,i-pos[a[i]]);} ans=max(ans,last[a[i]][belong[t]-1]-i); } ans=max(ans,Ans[belong[s]+1][belong[t]-1]); for(int i=L[belong[t]];i<=t;i++) { if(tim[a[i]]!=times) {tim[a[i]]=times;pos[a[i]]=i;} else {ans=max(ans,i-pos[a[i]]);} ans=max(ans,i-first[a[i]][belong[s]+1]); } return ans; } int main() { read(n); read(m); read(q); for(int i=1;i<=n;i++) read(a[i]); build(); while(q--) { read(l); read(r); printf("%d\n",query(l,r)); } return 0; }