【BZOJ5194】Snow Boots

【原题题面】传送门

【简化题意】

给定一个长度为n的序列。 有m次询问,每次询问给定两个数si,di。你一开始站在0,每次你可以走不超过di,但你到达的位置的数不能超过si。问能否走到n+1。 n,m<=100000。

【未讲之前】不知道这题哪里跟线段树挂钩了,感觉跟线段树八竿子打不着的关系23333

【题解】先根据s[i]的限制确定出哪些地方是不能走的,然后按s[i]从小到大排序,维护n上不能走的最长连续长度,因为每一次s[i]越来越大,那么限制越来越小。

还有弄清楚只要最长连续不能走的长度小于相应的d[i]就一定能走。这是因为每次能走的长度是1~d[i],所以我可以通过一定的调整使每次面临的都是最长连续长度的困难。

【讲了之后】按照s[i]排序离线这一点没想到哇。还有就是这题的切入口不是怎么线段树,而是理解题意之后想怎么维护不能走的最长连续长度,然后才想到可以用线段树。看网上还有题解说可以用set的,也可以实现的吧。

【具体实现】维护最长连续的长度,需要维护当前结点的范围内最长连续不能走的长度sz。

更新这个值需要max,lmax,rmax;

【code】

#include<bits/stdc++.h>
using namespace std;

inline int read()
{
    int N=0,C=0;char tf=getchar();
    for(;!isdigit(tf);tf=getchar())C|=tf=='-';
    for(;isdigit(tf);tf=getchar())N=(N<<1)+(N<<3)+(tf^48);
    return C?-N:N;
}

const int N=100010;
int n,q,ans[N];
struct nc
{
    int i,s,d;//id step deep
    friend bool operator <(nc a,nc b){return a.d==b.d?a.s<b.s:a.d<b.d;}//先放砖
    nc(int i=0,int s=0,int d=0):i(i),s(s),d(d){}
}a[N<<1];
struct tf
{
    int m,l,r,s;//max lmax rmax size
}t[N<<2];

void B(int l,int r,int p)
{
    t[p].m=t[p].l=t[p].r=t[p].s=r-l+1;
    if(l==r)return;
    int m=l+r>>1;
    B(l,m,p<<1),B(m+1,r,p<<1|1);
}

void U(int l,int r,int p,int x)
{
    if(l==r){t[p].m=t[p].l=t[p].r=0;return;}
    int m=l+r>>1;x<=m?U(l,m,p<<1,x):U(m+1,r,p<<1|1,x);

    t[p].l=t[p<<1].l==t[p<<1].s?t[p<<1].s+t[p<<1|1].l:t[p<<1].l;
    t[p].r=t[p<<1|1].r==t[p<<1|1].s?t[p<<1|1].s+t[p<<1].r:t[p<<1|1].r;

    t[p].m=max(t[p<<1].m,t[p<<1|1].m);
    t[p].m=max(t[p].m,t[p<<1].r+t[p<<1|1].l);
}

int main()
{
    n=read(),q=read(),B(1,n,1);
    for(int i=1,d;i<=n;++i)d=read(),a[i]=nc(i,0,d);
    for(int i=1,d,s;i<=q;++i)s=read(),d=read(),a[i+n]=nc(i,d,s);
    sort(a+1,a+n+q+1);

    for(int i=1;i<=n+q;++i)
    {
        if(!a[i].s)U(1,n,1,a[i].i);
        else ans[a[i].i]=t[1].m<a[i].s;
    }
    for(int i=1;i<=q;++i)printf("%d\n",ans[i]);

    return 0;
}
View Code

 

我的代码不知怎么就是调不出来#¥%……&*),上面是豆哥的。

 

猜你喜欢

转载自www.cnblogs.com/ve-2021/p/10387209.html