洛谷P3957 跳房子

普及组的题。。。
我不会。。。
题目

题解:

思路很简单,就是二分答案+dp+单调队列(线段树也可以),但是要注意细节,一个细节错了,一半分数就没了。
引用洛谷上某大佬的一段话:

发现答案的可行区间是单调的,所以二分答案,容易推出f[i]表示到达第i个格子的最大值,枚举上一步跳了多少来转移
然后仔细观察可以发现对于一个状态,如果有比他后面的状态比他答案大的话,显然不会优..于是可以单调队列进行优化
但是考虑对于一个点,他前面和他距离小于d-g的点是转移不到的,如果直接得出某个f[i]后就将他放进队列显然是错误的
考虑维护一个指针,表示当前最前面的一个没有加入单调队列的元素,当且仅当当前节点和他相差>=d-g的时候就让这个元素进队
显然这个指针也是单调的,也可以得出每个点在一次check中最多被指针指向一次;
处理完之后在一遍pop将所有和当前点距离>d+g的元素出队,取队首转移就可以了..算是一道不难不简单的套路题qwq

标程:

线段树:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=500003;
const ll p=-1999999999;
int n,d,k,i,ans,l,r,x[N],a[N],mid;
ll seg[N*4];
inline int read(){
    char c;int x=0,flag=1;
    do{c=getchar();if(c=='-') flag=-1;}while (c<'0' || c>'9');
    while (c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return flag*x;
}
void build(int t,int l,int r){
    seg[t]=p;
    if (l==r) return;
    int mid=l+r>>1;
    build(t<<1,l,mid);
    build(t<<1|1,mid+1,r);
}
void add(int t,int l,int r,int x,int val){
    if (l==r){
        seg[t]=val;
        return;
    }
    int mid=l+r>>1;
    if (x<=mid) add(t<<1,l,mid,x,val);
    else add(t<<1|1,mid+1,r,x,val);
    seg[t]=max(seg[t<<1],seg[t<<1|1]);
}
ll query(int t,int l,int r,int x,int y){
    if (x<=l && r<=y) return seg[t];
    int mid=l+r>>1;
    ll s=p;
    if (x<=mid) s=max(s,query(t<<1,l,mid,x,y));
    if (mid<y) s=max(s,query(t<<1|1,mid+1,r,x,y));
    return s;
}
int check(int mid){
    int q1=max(1,d-mid),q2=d+mid;
    int l=0,r=-1;
    build(1,0,n);
    add(1,0,n,0,0);
    for (int i=1;i<=n;i++){
        while (x[l]<x[i]-q2) l++;
        while (x[r+1]<=x[i]-q1) r++;
        if (l>r || l<0 || r<0) continue;
        ll s=query(1,0,n,l,r);
        if (s!=p){
            s+=a[i];
            add(1,0,n,i,s);
        }
        if (s>=k) return 1;
    }
    return 0;
}
int main(){
    n=read();d=read();k=read();
    for (i=1;i<=n;i++) x[i]=read(),a[i]=read();
    l=0;r=x[n];ans=-1;
    while (l<=r){
        mid=l+r>>1;
        if (check(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d",ans);
}

单调队列:

#include<bits/stdc++.h>
using namespace std;
const int N=500003;
typedef long long ll;
ll x[N],s[N],f[N];
int n,k,i,q[N],h,t,d,l,r,ans,mid;
int read(){
    char c;int x=0,f=1;
    do{c=getchar();if(c=='-')f=-1;}while(c<48||c>57);
    do x=(x<<1)+(x<<3)+(c^48),c=getchar();while(c>=48&&c<=57);
    return f*x;
}
void push(int x){
    while (h<=t && f[x]>=f[q[t]]) t--;
    q[++t]=x;
}
bool check(int g){
    int mn=max(d-g,1),mx=d+g,p=0;
    h=1;t=0;
    q[1]=0;
    for (int i=1;i<=n;i++){
        while (p<i && x[i]-x[p]>=mn) push(p++);
        while (h<=t && x[i]-x[q[h]]>mx) h++;
        if (h<=t) f[i]=f[q[h]]+s[i];
        else f[i]=-1e10;
        if (f[i]>=k) return 1;
    }
    return 0;
}
int main(){
    n=read();d=read();k=read();
    for (i=1;i<=n;i++) x[i]=read(),s[i]=read();
    l=0;r=x[n];ans=-1;
    while (l<=r){
        mid=l+r>>1;
        if (check(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d",ans);
}

猜你喜欢

转载自blog.csdn.net/xumingyang0/article/details/80329501