zoukankan      html  css  js  c++  java
  • BZOJ 2716 天使玩偶 CDQ分治

    题意:

    输入:

    输出:

    样例较长这里省略

    思路:

    算法:CDQ分治+树状数组

    首先,题目中存在绝对值,肯定是去掉比较容易,所以第一种情况是考虑分类讨论,即将查询的点当做原点,然后分四个象限分别拆绝对值,但这个时候会发现其实每个象限的算法区别不大,因此另一种想法就是每次都将所有的转到第三象限中,即通过加减转换,此时对于需要查询的(A)点,玩偶的位置(B)到达的距离都可以直接变为(dis=A_x+A_y-(B_x+B_y))由于(A_x+A_y)的值是固定的,需要dis最小,所以我们要找到(B_y+B_x)最大的点。

    由于我们直接设定的是在第三个象限,所以找到(B)点存在限制(B_y<=A_y)(B_x<A_x) 再加上一个时间限制,就是一个偏序问题。下面的思路是:先按照时间排序,在对(x)进行分治,用树状数组维护y的值。

    因为这题数据很卡,下面是几个优化
    1、对于时间排序,每一次sort的复杂度是(O(nlogn)) 一共要进行四次,那么复杂度就直接炸了,但是实际上我们可以记下最开始的排列, 每一次更改变换直接用最开始的排列。

    for(int i=1; i<=tot; i++)ans[i]=INF,P[i]=a[i];//这里的P数组就是用来记录初始序列
    CDQ(1,tot);
    for(int i=1; i<=tot; i++)P[i].x=Mx-P[i].x,a[i]=P[i];
    

    2、在CDQ中(x)的排序用可以考虑归并

    3、对于开始的([1,n])个数只有加入,所以可以直接排序返回

    if(r<=n){
    	sort(a+l,a+r+1);
    	return;
    }
    

    同时题目中还有个细节:(x,y)可能等于0,所以可以考虑都加一,防止树状数组陷入死循环。

    附代码+注释:

    #include<bits/stdc++.h>
    #define M 500005
    #define INF 1000000000
    #define N 1000005
    #define lowbit(x) (x&-x)
    using namespace std;
    int n,m,ans[M<<1],Mx;
    void Rd(int &res) {
    	res=0;
    	char c;
    	while(c=getchar(),c<48);
    	do res=(res<<1)+(res<<3)+(c-'0');
    	while(c=getchar(),c>=48);
    }
    struct node {
    	int op,x,y,id;
    	bool operator<(const node&_)const {
    		if(x!=_.x)return x<_.x;
    		return y<=_.y;
    	}
    } a[M<<1],p[M<<1],P[M<<1];
    struct Tree {
    	int mx[N];
    	void Init() {
    		memset(mx,0,sizeof(mx));
    	}
    	void add(int x,int y) {
    		while(x<=Mx)mx[x]=max(mx[x],y),x+=lowbit(x);
    	}
    	int find(int x) {
    		int res=0;
    		while(x)res=max(res,mx[x]),x-=lowbit(x);
    		return res;
    	}
    	void clear(int x) {
    		while(x<=Mx)mx[x]=0,x+=lowbit(x);
    	}
    } T;
    void CDQ(int l,int r) {
    	if(r<=n){
    		sort(a+l,a+r+1);
    		return;
    	}
    	if(l>=r)return;
    	int mid=(l+r)>>1;
    	int L=l,R=mid+1;
    	int now=l;
    	CDQ(l,mid),CDQ(mid+1,r);//正常的归并排序过程中加入计算
    	while(L<=mid&&R<=r) {
    		if(a[L]<a[R]) {
    			if(a[L].op==1)T.add(a[L].y,a[L].x+a[L].y);
    			p[now++]=a[L++];
    		} else {
    			if(a[R].op==2) {
    				int t=T.find(a[R].y);
    				if(t)ans[a[R].id]=min(ans[a[R].id],a[R].x+a[R].y-t);//t>0表示存在这样的点 因为开始已经将x,y都加了1
    			}
    			p[now++]=a[R++];
    		}
    	}
    	while(R<=r) {
    		if(a[R].op==2) {
    			int t=T.find(a[R].y);
    			if(t)ans[a[R].id]=min(ans[a[R].id],a[R].x+a[R].y-t);
    		}
    		p[now++]=a[R++];
    	}
    	for(int i=l; i<L; i++)if(a[i].op==1)T.clear(a[i].y);//撤销
    	while(L<=mid)p[now++]=a[L++];
    	for(int i=l; i<=r; i++)a[i]=p[i];
    }
    int main() {
    //	freopen("data.in","r",stdin);
    //	freopen("data.out","w",stdout);
    	Rd(n),Rd(m);
    	for(int i=1; i<=n; i++)Rd(a[i].x),Rd(a[i].y),a[i].op=1,a[i].x++,a[i].y++,Mx=max(Mx,max(a[i].x,a[i].y)),a[i].id=i;
    	for(int i=n+1; i<=m+n; i++)Rd(a[i].op),Rd(a[i].x),Rd(a[i].y),a[i].x++,a[i].y++,Mx=max(Mx,max(a[i].x,a[i].y)),a[i].id=i;
    	int tot=n+m;Mx++;
    	T.Init();
    	for(int i=1; i<=tot; i++)ans[i]=INF,P[i]=a[i];//下面是对四个方向的计算
    	CDQ(1,tot);
    	for(int i=1; i<=tot; i++)P[i].x=Mx-P[i].x,a[i]=P[i];
    	CDQ(1,tot);
    	for(int i=1; i<=tot; i++)P[i].y=Mx-P[i].y,a[i]=P[i];
    	CDQ(1,tot);
    	for(int i=1; i<=tot; i++)P[i].x=Mx-P[i].x,a[i]=P[i];
    	CDQ(1,tot);
    	for(int i=1; i<=tot; i++)if(ans[i]!=INF)printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    EL 自定义函数
    Linux 软件安装管理
    Linux 网络环境查看命令
    Linux 用户和用户组的命令
    Linux 用户和用户组进阶命令
    Linux 用户和用户组的基本命令
    将博客搬至CSDN
    U盘做系统启动盘(PE)时的文件格式选择 HDD ZIP FDD
    STM32 的几种输入输出模式
    define 中强制类型转换 && 浮点数后面带f
  • 原文地址:https://www.cnblogs.com/cly1231/p/10983225.html
Copyright © 2011-2022 走看看