2020牛客NOIP赛前集训营-提高组(第二场)T4移动

比赛时T3调了2个多小时 太菜了,导致没时间写T4,写了个暴力就交上去了,结果稳稳爆零(后来看变成了15?
本题解写了一半被手滑关掉了,不得不重写


进入正题,首先讲一下题意,你要从0走到n+1,其中有n个闸门,在一些时间段会关闭,每一秒可以向左,向右,不动,问最短时间(当x闸门t时刻打开,相邻的y闸门t+1时刻打开,就可以在时刻t从x移到y)
为了便于操作,将关闭时间转换成打开时间
这样一来,总共会有约2n个时间段,把它们看作新的时刻,此时每个新的时刻都对应着一个闸门(以下简称时刻)
我们记状态表示在时刻t,走到闸门p(t和p其实是对应的),当前所用时间(时刻t中的具体时间点)为x
考虑转移,当时,可以向左走,则下一个状态形如(为什么是x'而不是x+1?这是因为不一定恰好你刚到p就能走到p-1,可能需要等待一些时间)
如图:(nxt在这里表示p-1,同理也可以表示p+1,即下一个位置)
图片说明
当时刻包含中的任何一个时间点时,即可转移(中间可能会需要等待时间)
向右走同理

实现时,可以用一个堆,每次取出x最小的状态,进行转移
为了防止超时,我们记录在每个时刻到达每个闸门最短时间,当当前状态的时间大于当前最短时间时,不需要转移(一个时刻内时间越短越优,因为一个时刻内,闸门都是开的,可以等待,到达时间越短,转移可能越多)

code:(中间的注释会讲解一些细节)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>

using namespace std;
const int maxn=100020, INF=2000000000;

struct pj{
    int l, r; 
    int operator<(const pj& rhs) const {return l<rhs.l||l==rhs.l&&r<rhs.r;}
};
int n, m, b[maxn<<1], f[maxn<<1];
vector<pj> a[maxn], v;
struct tg{
    int t, p, x;
    int operator>(const tg& rhs) const {return x>rhs.x;}
};
priority_queue<tg, vector<tg>, greater<tg> > q;

int cmp(pj a, pj b) {return a.r<b.r;}

void move(tg u, int nxt) {//将状态u转移到nxt的位置
    vector<pj>::iterator it=upper_bound(a[nxt].begin(), a[nxt].end(), (pj){u.x, u.x}, cmp);
//第一个包含x+1的时刻
    while(it!=a[nxt].end()&&it->l<a[u.p][u.t-b[u.p]].r+2) {//保证时刻至少包含x+1到r+1中的一个
        int p=it-a[nxt].begin()+b[nxt];
        if(f[p]>max(it->l, u.x)+(it->l<=u.x)) {
//当it->l<=x时,max为x,但因为时间点为x时才到达,至少要到x+1才能到到nxt,所以加1
            q.push((tg){p, nxt, max(it->l, u.x)+(it->l<=u.x)});
            f[p]=min(f[p], max(it->l, u.x)+(it->l<=u.x));
        }    
        ++it;
    }
}

int main() {
    scanf("%d%d", &n, &m);
    for(int i=1, x, l, r; i<=m; ++i) {
        scanf("%d%d%d", &x, &l, &r);
        a[x].push_back((pj){l, r});
    }
    a[0].push_back((pj){0, INF}); b[0]=1;
    a[n+1].push_back((pj){0, INF});
    for(int i=1; i<=n; ++i) {//将关闭时间转化成打开时间
        sort(a[i].begin(), a[i].end());
        while(v.size()) v.pop_back();
        if(a[i].size()) {
            v.push_back((pj){0, a[i][0].l-1});
            for(int j=0; j<a[i].size()-1; ++j)
                v.push_back((pj){a[i][j].r+1, a[i][j+1].l-1});
            v.push_back((pj){a[i][a[i].size()-1].r+1, INF});
            a[i]=v;
        } else a[i].push_back((pj){0, INF});
        b[i]=b[i-1]+a[i-1].size();
    }
    b[n+1]=b[n]+a[n].size();
//    for(int i=0; i<=n+1; ++i, puts(""))
//        for(int j=0; j<a[i].size(); ++j) printf("(%d, %d)", a[i][j].l, a[i][j].r);
//    for(int i=1; i<=n; ++i) printf("%d ", b[i]); puts("");
    q.push((tg){1, 0, 0});
    memset(f, 0x3f, sizeof f); f[1]=0;
    while(!q.empty()) {
        tg u=q.top(); q.pop();
//        printf("%d %d %d\n", u.t, u.p, u.x);
//        for(int i=1; i<=b[n+1]; ++i) printf("%d ", f[i]); puts("");
        if(u.p>0) move(u, u.p-1);
        if(u.p<n+1) move(u, u.p+1);
    }
    printf("%d", f[b[n+1]]);
    return 0;
}

由于太菜,可能会有些错误,如果发现,欢迎指出