codeforces 827F. Dirty Arkady's Kitchen

26 篇文章 0 订阅
13 篇文章 0 订阅

给一个无向图,其中的无向边有一些出现时间,人一开始在1号点,每一时刻他都需要不断移动,通过一条无向边的时间是1,问最早能在什么时刻到达n号点

(其实并不知道题解在说什么,膜完代码yy了一个感觉差不多的)
最朴素的暴力即记录v[i][j]表示在i这个时间点能否到达j,然后枚举每条边转移,复杂度是O(Tm)的
我们考虑这样做慢在哪里:每条边的生效时间让他变成了T条边,于是边的总数是Tm的
注意到,一条边(u,v)的贡献是一个时间段,且如果到达u的时间是t,那么他会使v[t+1][v],v[t+3][v]..v[t+1+2k][v],v[t+2][u],v[t+4][u]..变成true,
改成true的这些时间点对于一个点来说奇偶性是相同的,又是时间连续的一段,
所以我们可以把奇偶性相同的一些时间点一起更新,将点分裂成奇数时刻和偶数时刻的点,把边拆成4条有向边:在偶数u->奇数v,奇数u->偶数v,v->u同理
更新的时候一次性把这条边能更新到的全部时间点都更新,就能使得边的数目变回到O(m)条
此时显然不能枚举时间去计算边的贡献,我们按照边产生贡献的时间顺序去计算贡献
为了更新每条边能贡献到的所有时刻,对于每条边需要求出一个dp[i]表示第一次到达这条边的时刻
对于一个点的所有能产生贡献的出边,他们产生贡献的顺序一定是按照出现时间升序的,对于每个点的出边按出现时刻升序排序(vector竟然兹瓷排序),弄个当前弧,对每个点维护他的出现时间段li[x]~ri[x](注意他的时间段不一定是连续的,可能中间会断开,只需要维护最后的一段)

具体操作的话,一开始从1的偶数点的第一条出边开始,每次判断当前这条边是否能产生贡献,尝试用它的dpi去更新这条边的结束点的时间段,如果成功更新且结束点不在队列里,将结束点和他的当前弧放进队列,权为(u,v)出现后结束点当前弧出现的最早时间,每次当前出发点的当前弧成功产生贡献或已经消失后,更新他的当前弧,将点和他新的当前弧放进队列,权也为这条边能够出现的最早时间

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
inline int num(const int x,const int p){return (x-1)*2+p;}
inline void up(int &x,const int &y){if(x<y)x=y;}
const int maxn = 1010000;
const int maxm = 1010000;

int n,m;
struct edge
{
    int y,l,r,rx;
    edge(){}
    edge(const int _y,const int _l,const int _r,const int _rx){y=_y;l=_l;r=_r;rx=_rx;}
};
vector<edge>v[maxn];
inline bool cmp(const edge x,const edge y){return x.l<y.l;}

struct node
{
    int x,t;
    node(){}
    node(const int _x,const int _t){x=_x;t=_t;}
};
inline bool operator <(const node &x,const node &y){return x.t>y.t;}
priority_queue<node>Q;

int li[maxn],ri[maxn],fir[maxn];
bool use[maxn];

int solve()
{
    for(int i=0;i<2*n;i++) li[i]=ri[i]=-1,fir[i]=0;
    li[0]=ri[0]=0;
    if(v[0].size()>0) Q.push(node(0,v[0][0].l));
    while(!Q.empty())
    {
        const node nx=Q.top(); Q.pop();
        int x=nx.x,p=x&1;
        use[x]=false;

        int &i=fir[x];
        const edge ne=v[x][i];
        if(ri[x]<ne.l) continue;
        if(li[x]<=ne.rx) 
        {
            const int y=ne.y;
            int rx=(ne.r&1)==p?ne.r-1:ne.r;
            if(ri[y]<ne.l+1)
            {
                li[y]=max(li[x]+1,ne.l+1),ri[y]=rx;
                if(!use[y]&&fir[y]!=v[y].size()) use[y]=true,Q.push(node(y,max(li[y],v[y][fir[y]].l)));
            }
            else if(ri[y]<rx)
            {
                ri[y]=rx;
                if(!use[y]&&fir[y]!=v[y].size()) use[y]=true,Q.push(node(y,max(li[y],v[y][fir[y]].l)));
            }
        }
        i++;
        if(i<v[x].size()) use[x]=true,Q.push(node(x,max(li[x],v[x][i].l)));

        if(li[2*n-2]!=-1||li[2*n-1]!=-1)
        {
            int xx=li[2*n-2],yy=li[2*n-1];
            if(xx==-1||yy==-1) return max(xx,yy);
            return min(xx,yy);
        }
    }
    return -1;
}

int main()
{
    read(n); read(m);
    if(n==1) { puts("0"); return 0; }
    for(int i=1;i<=m;i++)
    {
        int x,y,l,r; read(x); read(y); read(l); read(r);

        int lx=(l&1)?l+1:l,rx=(r&1)?r-1:r-2;
        if(lx<=rx) 
            v[num(x,0)].push_back(edge(num(y,1),lx,r,rx)),
            v[num(y,0)].push_back(edge(num(x,1),lx,r,rx));
        lx=(l&1)?l:l+1,rx=(r&1)?r-2:r-1;
        if(lx<=rx)
            v[num(x,1)].push_back(edge(num(y,0),lx,r,rx)),
            v[num(y,1)].push_back(edge(num(x,0),lx,r,rx));
    }
    for(int i=0;i<2*n;i++) sort(v[i].begin(),v[i].end(),cmp);

    printf("%d\n",solve());

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值