分治算法

基本思想:将序列分为 \([l,mid]\)\([mid+1,r]\),然后递归两边,同时再计算 \([l,mid]\)\([mid+1,r]\) 影响所产生的答案(满足单调性的话一般使用走指针)。

二维偏序

首先将所有元素按 \(x,y\) 排序。

然后递归两边,随后用两个指针 \(i\)\(j\)\(i\)\(l\)\(mid\)\(j\)\(mid+1\)\(r\),当 \(y_i>y_j\),说明 \(l\sim i-1\) 的点都不超过 \(j\),于是 \(ans_j\) 累加 \(i-l\),然后 \(j\) 后移一位;如果不是,那么我们的 \(i\) 往后移动一位。

这么做会有一个问题,我们的两边都应该按 \(y\) 排序,才能满足 \(y\) 的单调性,然而我们却按 \(x\) 排序,所以在统计完后重构 \(l\sim r\) 即可。

void f(int l,int r){
	if(l==r) return ;
	int m=(l+r)/2,i=l,j=m+1,c=l;
	f(l,m);f(m+1,r);
	while(i<=m||j<=r){
		if(j>r||(i<=m&&a[i].y<=a[j].y))
			b[c++]=a[i++];//类似于归并排序
		else ans[a[j].id]+=i-l,b[c++]=a[j++];
	}
	for(int i=l;i<=r;i++)a[i]=b[i];
}

平面最近点对

计算 \([l,mid]\)\([r,mid]\) 的最小点对,假设 \(d=\min(calc(l,mid),calc(mid+1,r))\)

首先先排除离 \(mid\) 距离大于 \(d\) 的点,这些点肯定不会更优。

然后把这些点按 \(y\) 排序,依次枚举所有点对,注意要判如果 \(y\) 轴差已经大于最优答案就跳出循环。

double merge(int left, int right) 
{
    double d = INF;
    if(left == right) return d;
    if(left + 1 == right) return dist(left, right);
    int mid = left + right >> 1;
    double d1 = merge(left, mid);
    double d2 = merge(mid + 1, right);
    d = min(d1, d2);
    int i, j, k = 0;
    for(i = left; i <= right; i++)
        if(fabs(S[mid].x - S[i].x) < d)
            temp[k++] = i;
    sort(temp, temp + k, cmps);
    for(i = 0; i < k; i++)
        for(j = i + 1; j < k && S[temp[j]].y - S[temp[i]].y < d; j++) 
		{
            double d3 = dist(temp[i], temp[j]);
            if(d > d3) d = d3;
        }
    return d;
}

渔网

考虑鱼 \([t,x]\)(在 \(t\) 时刻出现在 \(x\) 的鱼) 如果被网 \([T,y]\) (在 \(T\) 时刻撒在 \(y\) 的网)给网住,那应该满足什么条件。

\(t\) 时刻,网会蔓延 \(t-T\) 秒,那么其包含的便是 \([y-(t-T),y+(t-T)]\)。鱼被网住则 \(y-(t-T)\le x\le y+(t-T)\)

\(y-t+T\le x\)\(x\le y+t-T\)

\(y+T\le x+t\)\(x-t\le y-T\)(即 \(t-x\ge T-y\)

考虑新定义鱼的二元组 \((t-x,t+x)\) 与网的二元组 \((T-y,T+y)\),题目则问每个网的二元组 \((a,b)\) 有多少鱼的二元组 \((x,y)\) 满足 \(a\le x,b\le y\),二位偏序问题分治即可。

总结

当我们想对于每个 \(i\),都算出 \(\sum_{j<i\text{且}can(j,i)==true}giv(j)\)

void sol(int l,int r){
	if(l>=r)return ;
	int mid=l+r>>1;
	sol(l,mid);sol(mid+1,r);
	int ct=l,sum=0;
	for(int i=l,j=mid+1;i<=mid||j<=r;){
		if(i>mid||(j<=r&&can(i,j)==false))cnt1[p[j].id]+=sum,tmp[ct++]=p[j++];
		else sum+=giv(i),tmp[ct++]=p[i++];
	}
	rep(i,l,r)p[i]=tmp[i];
}

注意一定要归并排序,不然左右两边就不一定有单调性。

即:当i越界或j不越界但不合法,更新j的答案并把j后移。否则,计算i的贡献并后移。

posted @ 2023-09-20 22:47  include_c  阅读(27)  评论(0)    收藏  举报