Topcoder SRM 702 1000pts:FindingFriends(分治)

题解:
这道题很妙啊。

显然是要二分之后每个位置找到前后第一个合法位置,分别记为 L i , R i L_i,R_i Li,Ri,然后要求包含任意一个。

这个时候我们只需要找到 1 ∼ n 1\sim n 1n中间第一个非法的位置,然后递归下去做就好了,问题是怎么找到这个非法位置。

显然这是一个区间内的二维偏序,也就是三维偏序,可以KD树做或者线段树+主席树做,不过复杂度多了一个 log ⁡ n \log n logn,这时候有个非常高明的方法:考虑一下暴力从两端往中间扫,复杂度为 min ⁡ ( n − l , l ) \min(n-l,l) min(nl,l),总时间复杂度 T ( n ) = T ( n − l ) + T ( l ) + min ⁡ { n − l , l } = O ( n log ⁡ n ) T(n) = T(n-l)+T(l) + \min\{n-l,l\}=O(n \log n) T(n)=T(nl)+T(l)+min{nl,l}=O(nlogn),加上二分,时间复杂度为 O ( n log ⁡ n log ⁡ X ) O(n \log n \log X) O(nlognlogX)

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+50;

class FindingFriends {
	int n,lim,a[N],li[N],ri[N];
	int m,mx[N<<4],vec[N<<4];
	inline void build(int k,int l,int r,int t) {
		mx[k]=t; if(l==r) return;
		int mid=(l+r)>>1;
		build(k<<1,l,mid,t);
		build(k<<1|1,mid+1,r,t);
	}
	inline int ask(int k,int l,int r,int L,int R) {
		if(L<=l && r<=R) return mx[k];
		int mid=(l+r)>>1;
		if(R<=mid) return ask(k<<1,l,mid,L,R);
		else if(L>mid) return ask(k<<1|1,mid+1,r,L,R);
		else return max(ask(k<<1,l,mid,L,R),ask(k<<1|1,mid+1,r,L,R));
	}
	inline void inc(int k,int l,int r,int p,int v) {
		if(l==r) {mx[k]=v; return;}
		int mid=(l+r)>>1;
		(p<=mid) ? inc(k<<1,l,mid,p,v) : inc(k<<1|1,mid+1,r,p,v);
		mx[k]=max(mx[k<<1],mx[k<<1|1]);
	}
	inline bool solve(int l,int r) {
		if(r-l+1<lim) return false;
		for(int i=l,j=r;i<=j;i++,j--) {
			if(li[i]<l && ri[i]>r) return solve(l,i-1)|solve(i+1,r);
			if(li[j]<l && ri[j]>r) return solve(l,j-1)|solve(j+1,r);
		} return true;
	}
	inline int id(int v) {return lower_bound(vec+1,vec+m+1,v)-vec;}
	int L[N],R[N],I[N];
	inline bool check(int k) {
		m=0;
		for(int i=1;i<=n;i++)
			vec[++m]=a[i]-k, vec[++m]=a[i], vec[++m]=a[i]+k;
		sort(vec+1,vec+m+1); 
		m=unique(vec+1,vec+m+1)-vec-1;
		build(1,1,m,0); 
		for(int i=1;i<=n;i++) L[i]=id(a[i]-k), R[i]=id(a[i]+k), I[i]=id(a[i]);
		for(int i=1;i<=n;i++) {
			li[i]=ask(1,1,m,L[i],R[i]);
			inc(1,1,m,I[i],i);
		}
		build(1,1,m,-n-1);
		for(int i=n;i>=1;i--) {
			ri[i]=-ask(1,1,m,L[i],R[i]);
			inc(1,1,m,I[i],-i);
		} 
		return solve(1,n);
	}
    public:
    int shortestDistance(int len, vector<int> init, int A, int b, int c, int d, int e) {
        n=len; lim=e;
        for(int i=1;i<=init.size();i++) a[i]=init[i-1];
		for(int i=init.size()+1;i<=n;i++) a[i]=((long long)a[i-1]*A+(long long)b*(i-1)+c)%d;
		int l=0, r=1e9, ans;
		while(l<=r) {
			int mid=(l+r)>>1;
			if(check(mid)) ans=mid, r=mid-1;
			else l=mid+1;
		} return ans;
    }
};

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值