虽然T2 作法假了 不过稍微改一下 就能够写luogu另一道相似的题目node
屡次查询一个区间内 相同数字 最远的距离 c++
那么 咱们再考虑 查询区间内 相同数字 最近的距离git
看似特别相同的两个问题 咱们思考一下 是否可使用同一种方法写过ui
暴力写过是没有意义的 或许你抬杠你分比较高 这里不讨论了spa
确实 莫队和分块 均可以写过 可是 我确实是 想到了线段树的作法code
那么第一问是今天模拟赛的题目 我确实写了个一个线段树 很快就写完了 过了大小样例 就没再管 blog
下考场以后 才知道 样例的数据确实很水 让我很巧的避免了全部 我代码中错误的地方 排序
那么不妨我先来分析 第二问的作法 能够借鉴 CF522D Closest Equals 这个题目get
此时 查询最小距离 显然咱们能够直接 维护离他最近的距离便可it
由于 最小距离 具备最优性 考虑此时只维护左边第一个和他相同的数字出现的位置
对于 询问咱们不妨将询问离线 按照右端点从小到大排序 维护一下区间最小值便可 看code吧
//查询一个区间内 相同的数字的之间的最小距离 //首先将 询问离线 按右端点排序 对于每个x 咱们只关心 离他最近的x 那么维护左边第一个x出现的位置 //线段树维护区间最小值便可 #include <bits/stdc++.h> using namespace std; template<typename T>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