题意:给出1~n个点,i与i+1有权为1的边,有m个任务同时进行,你可以使用洪荒之力开一个传送门使某个点x到某个点y不需要代价,求完成所有任务的最小代价
题解:
二分+思维
嗯,如果抛开二分,这题基本上就是个纯思维题,乍一看很想运输计划的弱化版,但是运输计划断的是一条边,而这个题则是一条路径
二分最大路径长度,记下所有大于mid的路径,设传送门为(x,y),则这些路径的路线必定是:l -> x -> y -> r
于是我们考虑枚举x,对于某个任务,得出y的范围:mid-(abs(l-x))>=abs(y-r) -> r-mid+abs(l-x)<=y<=r+mid-abs(l-x)
于是得出了每个位置的x对于每个任务的y的范围,则问题转化判断所有的y是否有交
拆开上面式子的绝对值,发现需要分x<l和l<=x两种情况讨论,因为x是共有的未知项,所以只是比较大小的话可以忽略
那么可以预处理忽略掉x的y,最后判断是否有交即可
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define ll long long using namespace std; const int N = 500010; int n,m,L,ans=1<<30,l1[N],r1[N]; struct Node { int l,r; bool operator < (const Node &x) const { return l<x.l; } }p[N]; struct Dat {int l,r,l1,r1,l2,r2;}q[N]; int gi() { int x=0,o=1; char ch=getchar(); while(ch!=‘-‘ && (ch<‘0‘ || ch>‘9‘)) ch=getchar(); if(ch==‘-‘) o=-1,ch=getchar(); while(ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar(); return o*x; } bool check(int mid) { int cnt=0,top,l2,r2; for(int i=1; i<=m; i++) { if(p[i].r-p[i].l>mid) { cnt++; q[cnt].l=p[i].l,q[cnt].r=p[i].r; q[cnt].l1=p[i].r-mid+p[i].l,q[cnt].r1=p[i].r+mid-p[i].l;//l>x q[cnt].l2=p[i].r-mid-p[i].l,q[cnt].r2=p[i].r+mid+p[i].l;//l<=x } } l1[n+1]=l2=-1<<30,r1[n+1]=r2=1<<30,top=cnt; for(int i=n; i>=1; i--) { l1[i]=l1[i+1],r1[i]=r1[i+1]; while(top && q[top].l>i) { l1[i]=max(l1[i],q[top].l1); r1[i]=min(r1[i],q[top].r1); top--; } } top=1; for(int i=1; i<=n; i++) { while(top<=cnt && q[top].l<=i) { l2=max(l2,q[top].l2); r2=min(r2,q[top].r2); top++; } if(l1[i]-i<=r1[i]+i && l2+i<=r2-i) { if(r1[i]+i>=l2+i && l1[i]-i<=r2-i) return true; } } return false; } int main() { n=gi(),m=gi(); for(int i=1; i<=m; i++) { int x=gi(),y=gi(); p[i].l=min(x,y),p[i].r=max(x,y); L=max(L,p[i].r-p[i].l); } sort(p+1,p+m+1); int l=0,r=L,mid; while(l<=r) { mid=(l+r)>>1; if(check(mid)) ans=min(ans,mid),r=mid-1; else l=mid+1; } printf("%d", ans); return 0; }
时间: 2024-10-05 03:16:02