[CF827F]Dirty Arkady's Kitchen

Description

给出一张n个点m条边的无向图,每条边有存在时间区间[li,ri],一开始一只Akagi在1号点,每个时刻她都必须要从某个点走到另一个点,每一条边所花费的时间为1,求Akagi走到点n的最小时间。
n,m<=5*1e5

Solution

听说篡改题面可以出赤城
这道题看上去没有什么下手的地方,我们挖掘一下性质。
考虑暴力,vis[i][j]表示能否在j时刻到达点i。
可以发现一条边(u,v)可以更新vis[v][t+1],vis[v][t+3],vis[v][t+5]…..vis[u][t+2],vis[u][t+4],vis[u][t+6]….
也就是说能够更新的时间的奇偶性是相同的。
我们不妨把每个点拆成两个点,一个奇点,一个偶点,然后一条边拆成四条单向边。
把这O(m)条边扔进一个堆里,按照最早的出现时间l从小到大排序。
然后对于一个点x,维护L[x]表示x最晚能够在哪个时刻达到。
这样可以很轻易地判断一条边是否能够更新L数组。
如果不能更新就把这条边挂在起点,等到能够更新的时候再拿出来。
可以发现这样子一条边只会访问两次,所以总复杂度为O(m log m)

Code

#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a,opt) for(int i=lst[a][opt];i;i=nxt[i])
using namespace std;

int read() {
    char ch;
    for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
    int x=ch-'0';
    for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x;
}

const int N=5*1e5+5;

int to[N<<2],nxt[N<<2],R[N<<2],lst[N][2],cnt;
void Add(int x,int y,int opt,int r) {
    to[++cnt]=y;R[cnt]=r;nxt[cnt]=lst[x][opt];lst[x][opt]=cnt;
}

int n,m,x,y,l,r,L[N][2];

struct node{
    int x,y,l,r;
    friend bool operator < (node x,node y) {return x.l<y.l;}
};

multiset<node> s;

void Extend(int x,int l,int r,int opt) {
    L[x][opt]=max(L[x][opt],r);
    rep(i,x,opt) {
        node p;
        p.x=x;p.y=to[i];
        p.l=l;p.r=R[i];
        s.insert(p);
    }
    lst[x][opt]=0;
}

int main() {
    n=read();m=read();
    if (n==1) {puts("0");return 0;}
    fo(i,1,m) {
        x=read();y=read();l=read();r=read()-1;
        node p;
        p.x=x;p.y=y;p.l=l;p.r=r-(r-l)%2;s.insert(p);
        p.x=y;p.y=x;p.l=l;p.r=r-(r-l)%2;s.insert(p);
        p.x=x;p.y=y;p.l=l+1;p.r=r-!((r-l)%2);s.insert(p);
        p.x=y;p.y=x;p.l=l+1;p.r=r-!((r-l)%2);s.insert(p);
    }
    memset(L,255,sizeof(L));
    L[1][0]=0;
    while (!s.empty()) {
        node p=*s.begin();s.erase(s.begin());
        if (p.l>p.r) continue;
        int x=p.x,y=p.y,opt=p.l&1;
        if (L[x][opt]>=p.l) {
            if (y==n) {
                printf("%d\n",p.l+1);
                return 0;
            }
            Extend(y,p.l+1,p.r+1,opt^1);
        } else Add(x,y,opt,p.r);
    }
    puts("-1");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值