【JZOJ A组&省选】旅行

Description

悠悠岁月,不知不觉,距那传说中的pppfish晋级泡泡帝已是过 去数十年。数十年 中,这颗泡泡树上,也是再度变得精彩,各种泡泡 天才辈出,惊艳世人,然而,似乎 不论后人如何的出彩,在他们的头 顶之上,依然是有着一道身影而立。 泡泡帝,pppfish。 现在,pppfish即将带着被自己收服的无数个泡泡怪前往下一个 空间,而在前往下 一个空间的道路上,有N个中转站,和M条空间虫洞连接中转站(双向通道,可有重 边,可有环),然而,通过虫洞 是要一定的条件的,pppfish将手下所有泡泡怪编号为 1,2 … +∞,对于每个空间虫洞,有两个值L和R,表示此虫洞只允许编号从L到 R的泡 泡怪通过,pppfish现在在1号中转站,他想带尽可能多的泡 泡怪到达N号中转站,于是 pppfish找到了机智的你,希望你告诉 他最多可以带多少个泡泡怪,同时他还想知道所 有泡泡怪的编号(若 有多组解取字典序最小的一组 )

Input

第一行两个用空格隔开的整数N,M(2<=N<=1000,0<=M<=3000) 接下来M行,每行四个用空格隔开的整数a,b,l,r 表示在a,b中转站间有一个空间虫洞允许编号l~r的泡泡怪通过。(1<=a, b<=N,1<=l<=r<=1e6

Output

第一行一个整数ans,表示最多能携带的泡泡怪数量 接下来一行ans个用空格隔开的正整数,表示泡泡怪的编号,从小到大依次输出,如 果没有泡泡怪能通过只要输出“0”就可以了

Sample Input

Input1:
4 4
1 2 1 10
2 4 3 5
1 3 1 5
2 4 2 7
Input2:
2 2
1 2 1 3
1 2 4 6

Sample Output

Output1:
6
2 3 4 5 6 7
Output2:
3
1 2 3

Data Constraint

30%的数据 1 <= N,M <= 10
100%的数据 2 <= N <= 1000, 0 <= M <= 3000, 1 <= a, b <= N, 1 <= l <= r <= 10^6

思路

因为每条边可以通过的泡泡怪都是一段连续的区间
所以最优答案一定也是一段连续的区间,并且答案区间端点一定从各边端点区间中产生

由路径存在问题很容易联系到图的连通性问题

通过暴力枚举答案,确定可行的边集,利用并查集维护图的连通性
当1与n连通时,说明存在合法路径,更新答案即可
那么就得到了一个O(m^3) 的暴力算法

优化是很显然的,枚举左端点,右端点可以二分,
于是就有了一个O(m^2 log m)的暴力
然而时限只有1.5s,做到这样还不够

左端点确定的时候,右端点为什么要二分呢?
加边可以是个动态过程,如果当前图不连通,
可以再将一些新的边加入,并调整答案
于是就有了一个O(m^2)的算法

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f,maxn=3077;
struct A
{
    int l,r,u,v;
}a[maxn];
int f[maxn],n,m,s;
int cmp(A x,A y)
{
    return x.r<y.r;
}
int gf(int u)
{
    return u==f[u]?u:f[u]=gf(f[u]);
}
void get_s(int maxl)
{
    for(int i=1; i<=n; i++) f[i]=i;
    for(int i=m; i>=1; i--)
    {
        if(a[i].l>maxl) continue;
        int u=gf(a[i].u),v=gf(a[i].v);
        if(u!=v) f[u]=v;
        if(gf(1)==gf(n))
        {
            s=a[i].r; return;
        }
    }
    s=-inf;
}
int main()
{
    freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1; i<=m; i++) scanf("%d%d%d%d",&a[i].u,&a[i].v,&a[i].l,&a[i].r);
    sort(a+1,a+m+1,cmp);
    int l=inf,ans=0;
    for(int i=1; i<=m; i++)
    {
        int ll=a[i].l;
        get_s(ll);
        if(s-ll+1>ans)
        {
            ans=s-ll+1; l=ll;
        }
        else if(s-ll+1==ans) if(ll<l) l=ll;
    }
    printf("%d\n",ans);
    for(int i=l; i<=l+ans-1; i++) printf("%d ",i);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值