卡常暴搜——bzoj4239: 巴士走读

http://www.lydsy.com/JudgeOnline/problem.php?id=4239
第一次接触卡常数;
怕怕;
我们先建一个链表存储这个公交系统;
显然的,因为时间一定是递增的,所以不会有环;
那我们把1号点连出去的点全挑出来,连到n号点的点也取出来;
记录x,y
表示在x时出发,最早可以在y的时候到达学校;
那我们怎么记录呢?;
我们对每一个1号点连出去的点spfa一遍,就可以记录这个点到n点的最短时间了;
记录好x,y后;
我们把这些x,y按y重小到大,对于x取前缀最大值,然后就可以二分答案了;
以上是我自己想到的;
TLE…………………
然后我们就要常数优化;
但是spfa你能随手改嘛?
于是我把spfa变成了dfs;
我们想啊,每个1号点连出去的点都dfs一遍,坑定卡死;
其实对于每一条边,我们走一次就可以了;
因为这条边走出时,时间一定的;
那我们把原来的x,y;
变成在y时到达n点,最迟要在x时出发;
和原来的x,y是一样的;
我们把1号点连出去的点全挑出来,对他们排个序;
出发时间大的点先搜;
我们在每一天条边上加一个vi;
表示这一条边有没有访问过;
这样的话每条边只会经过一次;
TLE………………………
为什么呢?
因为虽然每条边只会经过一次,但是会访问很多次;
那我们在建图时候离线;
先把读入按出发时间排序;
然后建图;
这样对于每个点,如果按编表的顺序访问,时间是递增的;
那我们直接删边就好了,把访问到点的点全删了;
66666;

#include<iostream>
#include<cstdio>
#include<algorithm>
#define Ll long long
using namespace std;
const int M=3e5+5,N=1e5+5;
struct cs{int to,nxt,s,e;}a[M];
struct An{int x,y,z;}q[M];
struct an{int x,y;}t[M];
struct IN{int x,y,s,e;}in[M];
int head[N],ll,buf[30];
int x,y,z,n,m,s,e,top,tot;
bool cmp(an a,an b){return a.y<b.y;}
bool cmp2(An a,An b){return a.y<b.y;}
bool cmp3(IN a,IN b){return a.s<b.s;}
void init(int x,int y,int s,int e){
    a[++ll].to=y; a[ll].s=s; a[ll].e=e; 
    a[ll].nxt=head[x]; head[x]=ll;
}
void dfs(int x,int now,int S){
    for(int k=head[x];k;k=a[k].nxt){
        if(now>a[k].s){head[x]=k;return;}
        if(a[k].to==n){t[++top].x=S;t[top].y=a[k].e;}
        else dfs(a[k].to,a[k].e,S);        
    }
    head[x]=0;
}
int er(int k){
    int l=1,r=top,ans=0,mid;
    while(r>=l){
        mid=(l+r)/2;
        if(t[mid].y<=k){
            ans=max(ans,mid);
            l=mid+1;
        }else r=mid-1;
    }
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)scanf("%d%d%d%d",&in[i].x,&in[i].y,&in[i].s,&in[i].e);
    sort(in+1,in+m+1,cmp3);
    for(int i=1;i<=m;i++)init(in[i].x,in[i].y,in[i].s,in[i].e);
    for(int k=head[1];k;k=a[k].nxt)q[++tot].x=a[k].to,q[tot].y=a[k].s,q[tot].z=a[k].e;
    sort(q+1,q+tot+1,cmp2);
    for(int i=tot;i;i--)
        if(q[i].x!=n)dfs(q[i].x,q[i].z,q[i].y);else{
            t[++top].x=q[i].y;
            t[top].y=q[i].z;
        }   
    sort(t+1,t+1+top,cmp);
    for(int i=1;i<=top;i++)t[i].x=max(t[i].x,t[i-1].x);
    scanf("%d",&n);
    while(n--){
        scanf("%d",&m);
        int ans=er(m);
        if(!ans)printf("-1\n");else printf("%d\n",t[ans].x);
    }
}

6666666666666666666666666666666666666666666666

int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void write(int x)
{
    if (x<0) putchar('-'),x=-x;
    buf[0]=0;
    while (x) buf[++buf[0]]=x%10,x/=10;
    if (!buf[0]) buf[0]=1,buf[1]=0;
    while (buf[0]) putchar('0'+buf[buf[0]--]);
    printf("\n");
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值