把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CFGym102586L】Yosupo's Algorithm(分治+二维数点)

题目链接

  • 给定二维平面上 \(n\) 个红点和 \(n\) 个蓝点,每个点有一个点权。
  • \(q\) 次询问,每次给定 \(L,R\)。要求找到一个红点 \((rx,ry)\)(权值 \(rv\)) 和一个蓝点 \((bx,by)\)(权值 \(bv\)),满足:\(ry < by\)\(rx < L,bx > R\)\(rx > L,bx < R\)。求 \(rv+bv\) 的最大值。
  • \(1\le n\le10^5\)\(1\le q\le5\times10^5\),所有 \(x,L,R\) 各不相同,所有 \(y\) 各不相同

分治预处理

考虑 \(ry < by\) 这一限制,我们先将所有点按照 \(y\) 排序,然后分治,每次考虑红点在左区间、蓝点在右区间时的贡献。

发现 \(rx < L,bx > R\)\(rx > L,bx < R\) 等价于 \([rx < L]=[bx > R]\)。对于红点,记它的类型为 \([rx < L]\);对于蓝点,记它的类型为 \([bx > R]\)

于是可以用反证法证明,选中的红点和蓝点至少有一个是所在区间内权值最大的:如果选中的两个点都不是权值最大的,则选中的红点类型与权值最大的蓝点类型必然不同——否则就可以把选中的蓝点替换成权值最大的蓝点,同理选中的蓝点类型与权值最大的红点类型必然相同。但这样一来,由于选中的红点和蓝点类型相同,那么权值最大的红点和蓝点类型也就相同,选择它们肯定更优,产生矛盾。

因此只要将左区间最大红点与右区间所有蓝点视作一组可能的匹配关系,右区间最大蓝点与左区间所有红点视作一组可能的匹配关系即可。

二维数点求解答案

一组匹配关系可以表示为 \((rx,bx,v)\)

询问就是对于所有满足 \(rx < L,bx > R\) 的匹配关系和 \(rx > L,bx < R\) 的匹配关系询问 \(v\) 的最大值,也就是两个二维数点问题。

代码:\(O(n\log^2 n+q\log n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 100000
#define M 500000
#define SZ N*40
using namespace std;
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int ff,OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0,ff=1;W(!isdigit(oc=tc())) ff=oc^'-'?1:-1;W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));x*=ff;}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');}
	I void NA() {pc('-'),pc('1'),pc('\n');}
}using namespace FastIO;
int n;struct P {int x,y,w;I bool operator < (Cn P& o) Cn {return y<o.y;}}p[2*N+5];
int ct,dc,dv[SZ+5],Qt,ans[M+5];struct S {int x,y,w;I bool operator < (Cn S& o) Cn {return x<o.x;}}q[M+5],s[SZ+5];
struct TreeArray1
{
	int a[N+5];I void U(RI x,CI v) {W(x&&a[x]<v) a[x]=v,x-=x&-x;}I int Q(RI x) {RI t=0;W(x<=dc) t=max(t,a[x]),x+=x&-x;return t;}
}T1;
struct TreeArray2
{
	int a[N+5];I void U(RI x,CI v) {W(x<=dc&&a[x]<v) a[x]=v,x+=x&-x;}I int Q(RI x) {RI t=0;W(x) t=max(t,a[x]),x-=x&-x;return t;}
}T2;
I void Solve(CI l,CI r)//分治预处理
{
	#define Add(i,j) (s[++ct]=(S){p[i].x,p[j].x,p[i].w+p[j].w})
	if(l==r) return;RI i,x,mid=l+r>>1;
	for(x=-1,i=l;i<=mid;++i) p[i].x<0&&(!~x||p[x].w<p[i].w)&&(x=i);if(~x) for(i=mid+1;i<=r;++i) p[i].x>0&&(Add(x,i),0);//左区间最大红点与右区间所有蓝点
	for(x=-1,i=mid+1;i<=r;++i) p[i].x>0&&(!~x||p[x].w<p[i].w)&&(x=i);if(~x) for(i=l;i<=mid;++i) p[i].x<0&&(Add(i,x),0);//右区间最大蓝点与左区间所有红点
	Solve(l,mid),Solve(mid+1,r);
}
int main()
{
	RI i;for(read(n),i=1;i<=n;++i) read(p[i].x,p[i].y,p[i].w);for(i=1;i<=n;++i) read(p[n+i].x,p[n+i].y,p[n+i].w);
	#define GV(x) (lower_bound(dv+1,dv+dc+1,x)-dv)
	for(sort(p+1,p+2*n+1),Solve(1,n<<1),sort(s+1,s+ct+1),i=1;i<=ct;++i) dv[i]=s[i].y;//按点按y排序然后分治;将关系按第一维排序
	for(sort(dv+1,dv+ct+1),dc=unique(dv+1,dv+ct+1)-dv-1,i=1;i<=ct;++i) s[i].y=GV(s[i].y);//离散化
	for(read(Qt),i=1;i<=Qt;++i) read(q[i].x,q[i].y),q[i].w=i;sort(q+1,q+Qt+1);//按询问按第一维排序
	RI j;for(i=j=1;i<=Qt;ans[q[i].w]=T1.Q(GV(q[i].y+1)),++i) W(j<=ct&&s[j].x<q[i].x) T1.U(s[j].y,s[j].w),++j;//第一种二维数点
	for(i=Qt,j=ct;i;ans[q[i].w]=max(ans[q[i].w],T2.Q(GV(q[i].y)-1)),--i) W(j&&s[j].x>q[i].x) T2.U(s[j].y,s[j].w),--j;//第二种二维数点
	for(i=1;i<=Qt;++i) ans[i]?writeln(ans[i]):NA();return clear(),0;
}
posted @ 2022-01-26 16:41  TheLostWeak  阅读(194)  评论(0编辑  收藏  举报