Tyvj4866:摆摊 (线段树)

xiaoxiao2021-02-28  10

题目传送门:http://tyvj.cn/p/4866


题目分析:做清北学堂NOIP模拟赛1的时候见到这题,一开始想了好久只会 O(Qmlog(n)) 的莫队,就是一边做莫队一边用线段树维护最左边的两个空摊位。后来tututu过来看到这题,转了两圈立马就想出了 O(Qlog(m)) 的正解做法……

我们先用 f[i] 表示 a[1] ~ a[i] 的摊位都被占用时答案是多少,很明显 f[i] 单调不降,一开始单调往后扫一遍即可得到每一个f值。现在我们考虑区间左端点右移时对f值的影响,比如当左端点变为2的时候, a[1] 就空出来了。假设 a[1]>1 ,且 a[j]=a[1]1 ,那么 f[i](2<=i<j) 都可以考虑更新为 a[1]1 (若j不存在则j=m+1);假设 a[1]<n ,若 a[k]=a[1]+1 ,那么 f[i](2<=i<k) 都可以考虑更新为 a[1] 。这样f数组就可以用一棵线段树维护。

我听了之后很激动,然后码了45min,结果发现一直调不对手出的小数据,最后快到12点了,只好交上错的程序,再后来又调了15min发现两个m打成了n……

几天后我又交了我的程序上tyvj,结果爆10了,下了个小数据发现a数组的值可能重复,这样我们还要开一个数组记对于每一个i,离它最近的 a[j]=a[i](j>i) 是哪个,记 Next[i]=j ,然后在线段树更新的时候要分别跟 Next[i] 取min。

听说这题没有卡莫队,所以其实我一开始的方法也可能AC,结果中途转去写了tututu的方法,我就爆零了。Coming和Ab.Ever也写了这种方法,一个爆10一个爆0。我这次比赛500分,就差这题的分,唉。 Ab.Ever:早知码暴力,不听tu装逼。


CODE:

#include<iostream> #include<string> #include<cstring> #include<cmath> #include<cstdio> #include<cstdlib> #include<stdio.h> #include<algorithm> using namespace std; const int maxn=200100; struct data { int l,r,t; } ask[maxn]; int tree[maxn<<2]; int b[maxn]; int vis[maxn]; int Next[maxn]; int val[maxn]; int a[maxn]; int ans[maxn]; int n,m,q; bool Comp(data x,data y) { return ( x.l<y.l || ( x.l==y.l && x.r<y.r ) ); } void Build(int root,int L,int R) { if (L==R) { tree[root]=b[L]; return; } int mid=(L+R)>>1; int Left=root<<1; int Right=Left|1; Build(Left,L,mid); Build(Right,mid+1,R); tree[root]=maxn; } void Update(int root,int L,int R,int x,int y,int v) { if ( y<L || R<x ) return; if ( x<=L && R<=y ) { tree[root]=min(tree[root],v); return; } int mid=(L+R)>>1; int Left=root<<1; int Right=Left|1; Update(Left,L,mid,x,y,v); Update(Right,mid+1,R,x,y,v); } void Down(int root) { int Left=root<<1,Right=Left|1; tree[Left]=min(tree[Left],tree[root]); tree[Right]=min(tree[Right],tree[root]); tree[root]=maxn; } int Query(int root,int L,int R,int x) { if ( L==x && x==R ) return tree[root]; Down(root); int mid=(L+R)>>1; int Left=root<<1; int Right=Left|1; if (x<=mid) return Query(Left,L,mid,x); else return Query(Right,mid+1,R,x); } int main() { freopen("stall.in","r",stdin); freopen("stall.out","w",stdout); scanf("%d%d%d",&n,&m,&q); for (int i=1; i<=m; i++) scanf("%d",&a[i]); for (int i=1; i<=q; i++) scanf("%d%d",&ask[i].l,&ask[i].r),ask[i].t=i; sort(ask+1,ask+q+1,Comp); for (int i=1; i<=n; i++) val[i]=m+1; for (int i=m; i>=1; i--) Next[i]=val[ a[i] ],val[ a[i] ]=i; b[1]=1; if (a[1]==1) b[1]=2; vis[ a[1] ]++; for (int i=2; i<=m; i++) { vis[ a[i] ]++; b[i]=b[i-1]; while ( vis[ b[i] ] || vis[ b[i]+1 ] ) b[i]++; } Build(1,1,m); int tail=0; while (ask[tail+1].l==1) tail++,ans[ ask[tail].t ]=Query(1,1,m,ask[tail].r); for (int L=2; L<=m; L++) { int x=a[L-1]; val[x]=Next[L-1]; if (x) Update(1,1,m,L-1, min(val[x-1],val[x])-1 ,x-1); if (x<=n) Update(1,1,m,L-1, min(val[x],val[x+1])-1 ,x); while (ask[tail+1].l==L) tail++,ans[ ask[tail].t ]=Query(1,1,m,ask[tail].r); } for (int i=1; i<=q; i++) if (ans[i]<n) printf("%d %d\n",ans[i],ans[i]+1); else printf("-1 -1\n"); return 0; }
转载请注明原文地址: https://www.6miu.com/read-200015.html

最新回复(0)