Tyvj4866:摆摊 (线段树)

来源:互联网 发布:激光脉冲价格知乎 编辑:程序博客网 时间:2024/06/10 19:01

题目传送门: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;}
原创粉丝点击