题目链接
题解
首先不难想到可以二分枚举答案,然后再判断枚举的答案是否合法,因此关键就在于这个怎么能够快速地判断是否合法了。
根据题意我们可以发现一个景点最多有两种状态,要么不是商业区(值为a[i],用i来表示),要么是商业区(值为b[i],用i+n表示)。
因此可以联想到图论中经典的2-SAT问题,之后就只需要考虑怎么建图了。
我们可以直接枚举每条边的两个点的所有状态,即x不是商业区和y不是商业区,x是商业区和y不是商业区,x不是商业区和y是商业区,以及x是商业区和y是商业区这四种情况。之后就需要判断这四个情况是否合法(即两个点的差值的绝对值是否小于等于我们二分枚举的答案mid)。
但是这里我们不能直接判断是否满足小于等于的条件,这样二分的时候不好判断,我们需要反向思考。
当两个点的差值的绝对值大于mid时,即表示这两个点的状态不合法,因此只能一个点取原来的状态,另一个点取相反的状态。
举个例子,当x选择a数组(即不是商业区),y选择a数组(不是商业区)时不符合条件(差值的绝对值大于mid)时。
那就只能满足下面两个条件:
要么如果x选择a数组,y就必须选择b数组。
要么如果y选择a数组,x就必须选择b数组。
转换为代码的话就是
if(abs(a[x]-a[y])>mid) add(x,y+n),add(y,x+n);
剩下的三种情况类比上面进行建图就行了,然后就是2-sat的模板,对于这个算法不懂的可以参考:https://www.luogu.com.cn/blog/85514/post-2-sat-xue-xi-bi-ji
代码
#include <bits/stdc++.h> #define PI atan(1.0)*4 #define rp(i,s,t) for (register int i = (s); i <= (t); i++) #define RP(i,t,s) for (register int i = (t); i >= (s); i--) #define sc(x) scanf("%d",&x) #define scl(x) scanf("%lld",&x) #define ll long long #define ull unsigned long long #define mst(a,b) memset(a,b,sizeof(a)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define pii pair<int,int> #define pll pair<ll,ll> #define pil pair<int,ll> #define m_p make_pair #define p_b push_back #define ins insert #define era erase #define INF 0x3f3f3f3f #define LINF 0x3f3f3f3f3f3f3f3f #define dg if(debug) #define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"; using namespace std; int debug = 0; ll gcd(ll a,ll b){ return b?gcd(b,a%b):a; } ll lcm(ll a,ll b){ return a/gcd(a,b)*b; } inline int read(){ int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; } const int N = 1e6+7; const int maxn=1e6+7; const int maxm=2e6+7; int a[N],b[N]; int u[N],v[N]; int n,m; struct node{ int to,nex; }e[maxm]; int dfncnt,sccnum,scc[maxn],dfn[maxn],low[maxn],s[maxn],top; int head[maxn],cnt; void add(int u,int v){//链式前向星 e[++cnt].to=v,e[cnt].nex=head[u],head[u]=cnt; } void tarjan(int u){//tarjan求强联通 dfn[u]=low[u]=++dfncnt; s[++top]=u; for(int i=head[u];i;i=e[i].nex){ int v=e[i].to; if(!dfn[v]){ tarjan(v); low[u]=min(low[u],low[v]); } else if(!scc[v]){ low[u]=min(low[u],low[v]); } } if(dfn[u]==low[u]){//弹栈 ++sccnum; while(s[top]!=u){ scc[s[top]]=sccnum;//强连通分量编号 top--; } scc[s[top]]=sccnum; top--; } } void init(){ memset(head,-1,sizeof head); memset(e,0,sizeof e); memset(scc,0,sizeof scc); memset(dfn,0,sizeof dfn); memset(low,0,sizeof low); memset(s,0,sizeof s); cnt=dfncnt=top=sccnum=0; } int check(int mid){ init(); rp(i,1,m){ int x=u[i],y=v[i]; if(abs(a[x]-a[y])>mid) add(x,y+n),add(y,x+n); if(abs(a[x]-b[y])>mid) add(x,y),add(y+n,x+n); if(abs(b[x]-a[y])>mid) add(x+n,y+n),add(y,x); if(abs(b[x]-b[y])>mid) add(x+n,y),add(y+n,x); } rp(i,1,2*n) if(!dfn[i]) tarjan(i); rp(i,1,n) if(scc[i]==scc[i+n]) return 0; return 1; } void solve(){ n=read(),m=read(); rp(i,1,n) a[i]=read(); rp(i,1,n) b[i]=read(); rp(i,1,m) u[i]=read(),v[i]=read(); int l=0,r=INF; int ans=0; while(l<=r){ int mid=(l+r)/2; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } cout<<ans<<endl; } int main(){ //ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); #ifdef ONLINE_JUDGE #else freopen("in.txt", "r", stdin); //debug = 1; #endif time_t beg, end; //if(debug) beg = clock(); solve(); /* if(debug) { end = clock(); printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC); } */ return 0; }