首页 > 其他分享 >【XSY3979】数据结构(分治,剪枝)

【XSY3979】数据结构(分治,剪枝)

时间:2022-10-31 07:33:40浏览次数:70  
标签:剪枝 XSY3979 int max 蓝点 mid 坐标 内权值 数据结构

题面

数据结构

题解

挺神奇的一道题。

正解是对 \(y\) 坐标分治。

每次考虑 \(y\) 坐标在 \([l,mid]\) 范围内的红点和 \(y\) 坐标在 \([mid+1,r]\) 范围内的蓝点匹配成点对的贡献。

考场上尝试过这种做法,但发现时间复杂度不对劲就弃掉了。

但有一种极妙的剪枝方法,它结合了题目特殊的询问条件,得出并依靠了下面的这条结论:

只有选择的红点为 \(y\) 坐标在 \([l,mid]\) 范围内权值最大的那个红点、或者选择的蓝点为 \(y\) 坐标在 \([mid+1,r]\) 范围内权值最大的那个蓝点,这样的点对才有贡献。

证明:

考虑反证法:

假设询问 \(i\) 不满足上述的结论,不妨设询问 \(i\) 为 \(L_i,R_i\)。

假设询问 \(i\) 找到的最大点权和的点对是在 \(y\) 坐标在 \([l,mid]\) 范围内的红点 \(R\) 和 \(y\) 坐标在 \([mid+1,r]\) 范围的蓝点 \(B\),且 \(R\) 不是 \(y\) 坐标在 \([l,mid]\) 范围内权值最大的红点,\(B\) 也不是 \(y\) 坐标在 \([mid+1,r]\) 范围内权值最大的蓝点。

那么我们找到 \(y\) 坐标在 \([l,mid]\) 范围内权值最大的红点,记为 \(R_{max}\);找到 \(y\) 坐标在 \([mid+1,r]\) 范围内权值最大的蓝点,记为 \(B_{max}\)。

注意到询问的第二个条件是要求两个点均在区间 \([L_i,R_i]\) 内或两个点均在区间 \([L_i,R_i]\) 外。(注意题目保证了所有的 \(x_i,L_i,R_i\) 各不相同)

不妨设 \(B,R\) 均在区间内(均在区间外的情况同理):

  • 显然如果选 \(B,R_{max}\) 符合询问的第二个条件的话,就会选择 \(B,R_{max}\),因为这么选更优。但既然没有选择,说明 \(R_{max}\) 在区间外。
  • 显然如果选 \(B_{max},R\) 符合询问的第二个条件的话,就会选择 \(B_{max},R\),因为这么选更优。但既然没有选择,说明 \(B_{max}\) 在区间外。

所以我们得出结论:\(B_{max}\) 和 \(R_{max}\) 均在区间外。

那么选 \(B_{max}\) 和 \(R_{max}\) 显然是更优的,与假设矛盾。

所以结论正确。

所以我们可以按最开始说的对 \(y\) 分治,而合并两个子区间时有贡献的点对个数是 \(O(\text{当前区间长度})\) 级别的,所以总的有贡献的点对是 \(O(n\log n)\) 级别的。

然后可以把询问离线排序,变成简单的二维数点问题,可以用树状数组解决。

时间复杂度 \(O(n\log ^2n+q\log n)\)。

代码如下:

#include<bits/stdc++.h>

#define N 100010
#define M 500010

using namespace std;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

struct Point
{
	int x,y,val;
	bool opt;
}p[N<<1];

inline bool operator < (Point a,Point b)
{
	return a.y<b.y;
}

struct Query
{
	int l,r,id;
}q[M];

inline bool operator < (Query a,Query b)
{
	return a.l<b.l;
}

int n,nn,Q,b1[N],b2[N],val1[N],val2[N];
int lowbit[N],c1[N],c2[N];
int Ans[M];

vector<int>v[N];

void solve(int k,int l,int r)
{
	if(l==r) return;
	int mid=(l+r)>>1,lc=k<<1,rc=k<<1|1;
	solve(lc,l,mid),solve(rc,mid+1,r);
	int max1=0,max2=0;
	for(int i=l;i<=mid;i++) 
		if(!p[i].opt&&(!max1||p[i].val>p[max1].val)) max1=i;
	for(int i=mid+1;i<=r;i++)
		if(p[i].opt&&(!max2||p[i].val>p[max2].val)) max2=i;
	if(!max1||!max2) return;
	for(int i=l;i<=mid;i++)
		if(!p[i].opt) v[p[i].x].push_back(p[max2].x);
	for(int i=mid+1;i<=r;i++)
		if(i!=max2&&p[i].opt) v[p[max1].x].push_back(p[i].x);
}

inline void update1(int x,int y)
{
	for(;x;x-=lowbit[x]) c1[x]=max(c1[x],y);
}

inline int query1(int x)
{
	int ans=-1;
	for(;x<=n;x+=lowbit[x]) ans=max(ans,c1[x]);
	return ans;
}

inline void update2(int x,int y)
{
	for(;x<=n;x+=lowbit[x]) c2[x]=max(c2[x],y);
}

inline int query2(int x)
{
	int ans=-1;
	for(;x;x-=lowbit[x]) ans=max(ans,c2[x]);
	return ans;
}

int main()
{
	memset(c1,-1,sizeof(c1));
	memset(c2,-1,sizeof(c2));
	memset(Ans,-1,sizeof(Ans));
	n=read(),nn=n<<1;
	for(int i=1;i<=n;i++) lowbit[i]=i&-i;
	for(int i=1;i<=n;i++)
		b1[i]=p[i].x=read(),p[i].y=read(),p[i].val=read(),p[i].opt=0;
	for(int i=1;i<=n;i++)
		b2[i]=p[n+i].x=read(),p[n+i].y=read(),p[n+i].val=read(),p[n+i].opt=1;
	sort(b1+1,b1+n+1),sort(b2+1,b2+n+1);
	for(int i=1;i<=n;i++)
	{
		p[i].x=lower_bound(b1+1,b1+n+1,p[i].x)-b1;
		val1[p[i].x]=p[i].val;
		p[n+i].x=lower_bound(b2+1,b2+n+1,p[n+i].x)-b2;
		val2[p[n+i].x]=p[n+i].val;
	}
	sort(p+1,p+nn+1);
	solve(1,1,nn);
	Q=read();
	for(int i=1;i<=Q;i++)
	{
		q[i].l=read(),q[i].r=read(),q[i].id=i;
		q[i].l=lower_bound(b1+1,b1+n+1,q[i].l)-b1;
		q[i].r=lower_bound(b2+1,b2+n+1,q[i].r)-b2;
	}
	sort(q+1,q+Q+1);
	int tmp=1;
	for(int i=1;i<=n;i++)
	{
		while(tmp<=Q&&q[tmp].l==i)
		{
			Ans[q[tmp].id]=query1(q[tmp].r);
			tmp++;
		}
		for(int j=0,size=v[i].size();j<size;j++)
			update1(v[i][j],val1[i]+val2[v[i][j]]);
	}
	while(tmp<=Q)
	{
		Ans[q[tmp].id]=query1(q[tmp].r);
		tmp++;
	}
	tmp=Q;
	while(q[tmp].l>n) tmp--;
	for(int i=n;i>=1;i--)
	{
		for(int j=0,size=v[i].size();j<size;j++)
			update2(v[i][j],val1[i]+val2[v[i][j]]);
		while(tmp&&q[tmp].l==i)
		{
			Ans[q[tmp].id]=max(Ans[q[tmp].id],query2(q[tmp].r-1));
			tmp--;
		}
	}
	for(int i=1;i<=Q;i++)
		printf("%d\n",Ans[i]);
	return 0;
}
/*
2
-3 1 1
-6 3 10
3 4 100
5 2 1000
5
-5 4
-2 6
-4 1
-10 10
-1 2
*/

标签:剪枝,XSY3979,int,max,蓝点,mid,坐标,内权值,数据结构
From: https://www.cnblogs.com/ez-lcw/p/16842955.html

相关文章

  • 数据结构与算法-树
    树的表示与术语节点的度、树的度、叶子节点、父亲节点、兄弟节点、堂兄节点、祖先节点、子孙节点、节点层次、树的深度、路径、路径长度、分支...二叉树二叉树的性质......
  • 【数据结构】(一)线性表
    约定:Status是函数的返回值类型,其值是函数结果状态代码typedef描述存储结构的类型定义ElemType表示数据元素类型   一.顺序表1.1顺序表的初始化动态分......
  • 【数据结构-数组】数组的相关算法
    目录1无序数组的排序——快速排序1.1升序排序1.2降序排序2有序数组的查找——折半查找(二分查找)2.1升序数组的查找2.2降序数组的查找3有序数组的合并——归并思想3.1......
  • 数据结构 玩转数据结构 4-6 使用链表实现栈
    0课程地址https://coding.imooc.com/lesson/207.html#mid=13449 1重点关注1.1使用链表实现栈代码解析见3.1  2课程内容3......
  • 数据结构 玩转数据结构 6-1 为什么要研究树结构
    0课程地址https://coding.imooc.com/lesson/207.html#mid=13454 1重点关注1.1为什么研究树结构高效    2课程内容3......
  • 常见的hash数据结构
    遍历hash表是一种比较简单和直观的数据结构,在查找时也有很好的性能。但是hash表不能提供有序遍历,这个是其特性决定,所以不足为奇。但是,更为实际的一个问题是如果遍历整个ha......
  • 数据结构之环形队列
    概述队列是一种具有先进先出(FIFO)的数据类型,可以使用多种数据结构来实现队列:数组和链表。简单队列的应用场景比较有限,于是那些牛人们就发明一些复杂的队列:环形队列双端队列优......
  • 驱动开发之基本数据结构
    根据MSDN的介绍,自己对一些基本结构做一些翻译,帮助自己理解。驱动对象 DRIVER_OBJECTtypedefstruct_DRIVER_OBJECT{CSHORTType;CSHORT......
  • 数据结构—第二章线性表习题
    (1)B(2)A(3)B(4)A(5)D(6)B(7)C(8)A(9)B(10)D(11)C(12)D(13)D(14)A(15)C(1)voidMergeList(LinkList&La,LinkList&Lb,LinkList&Lc){//将两个递增的有序链表La和Lb合并为一个递增的有序链表Lc......
  • 20221027数据结构与算法之线性表——顺序表
    广州疫情被封区,在家学习#pragmawarning(disable:4996)#include<stdio.h>#include<stdlib.h>//动态顺序表的实现typedefintdata_t;typedefstructSeqList{data_t*da......