2018年10月24日提高组 T3 TRAVEL

大意

给定每个点之间连接道路的限制 l , r l,r l,r,求出从1到 n n n可以带走最大的区间,如果有多组解,输出字典序最小的一组


思路

首先很容易想到 d f s dfs dfs

#include<algorithm>
#include<vector>
#include<cstdio>
#define ri register int
using namespace std;int n,m,head[1001],tot,x,y,l,r,ansl,ansr;
struct node{int p,l,r;};
vector<node>e[6001];
bool vis[1001];
inline void dfs(ri x,ri L,ri R)//计算所有从1到n的路,求答案
{
	if(R-L<ansr-ansl||(R-L==ansr-ansl&&L>=ansl)) return;//最优化剪枝
	if(x==n)
	{
		if(R-L>ansr-ansl||R-L==ansr-ansl&&L<ansl)
		{
			ansl=L;
			ansr=R;
		}
		return;
	}
	for(ri i=0;i<e[x].size();i++)
	{
		int y=e[x][i].p,xl=e[x][i].l,xr=e[x][i].r;
		if(!vis[y]) {vis[y]=true;dfs(y,max(xl,L),min(xr,R));vis[y]=false;}
	}
	return;
}
signed main()
{
	scanf("%d%d",&n,&m);
	for(ri i=1;i<=m;i++)
	{
		scanf("%d%d%d%d",&x,&y,&l,&r);
		e[x].push_back((node){y,l,r});
		e[y].push_back((node){x,l,r});
	}
	vis[1]=true;dfs(1,1,1000000);
	printf("%d\n",ansr-ansl+1);
	for(ri i=ansl;i<=ansr;i++) printf("%d ",i);
}

于是愉快的 T l e Tle Tle

我们想到了一种固定边界的方法,假设我们枚举左边界,然后二分右边界,然后用一种算法判断是否可行(本人用的是并查集),但是这样的复杂度是 O ( 1 0 6 l o g 1 0 12 ) O(10^6log10^{12}) O(106log1012) T l e Tle Tle

然后我们发现左右边界永远都是所有路中的,于是我们可以只枚举出现的左右边界,复杂度为 O ( m 2 ) O(m^2) O(m2)可以过


代码

#include<cstdio>
#include<algorithm>
#define ri register int
using namespace std;int f[1001],n,m,ans,ansl,l,r;
struct node{int l,r,u,v;}a[3001];
inline int find(ri x){return x==f[x]?x:f[x]=find(f[x]);}//并查集
inline bool cmp(node x,node y){return x.r<y.r;}//排序
inline void check(ri x)//判断是否可以到达
{
	for(ri i=1;i<=n;i++) f[i]=i;
	for(ri i=m;i>0;i--)
	{
		if(a[i].l>x) continue;//超出了我枚举的限制,就不走了
		int u=find(a[i].u),v=find(a[i].v);
		if(u!=v) f[u]=v;
		if(find(1)==find(n))//可以到达
		{
			r=a[i].r;return;//因为我的r是排了序的,所以前面搜到的一定是最优的
		}
	}
	r=-1000000000;return;//没找到
}
signed main()
{
	scanf("%d%d",&n,&m);
	for(ri 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+1+m,cmp);//排序
	for(ri i=1;i<=m;i++)
	{
		l=a[i].l;
		check(l);
		if(r-l+1>ans||r-l+1==ans&&l<ansl)//比较
		{
			ans=r-l+1;
			ansl=l;
		}
	}
	printf("%d\n",ans);
	for(ri i=ansl;i<ansl+ans;i++) printf("%d ",i);//输出
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值