【GXOI/GZOI2019】特技飞行(扫描线)(线段树)(曼哈顿距离转切比雪夫距离)

传送门


感觉是一道和计算几何没有什么关系的计算几何

题解:

容易注意到其实题目分为完全不相干的两个部分。

首先是算 c c c的出现次数,然后是计算最多和最少能够出现多少个 a a a

交换的最多次数其实就是交点个数,因为交点个数就是逆序对个数。

或者可以感性理解一下,如果在每个相交的地方都交换方向,显然全程所有飞机的相对顺序都不会改变,最后结果就一定符合要求。

根据一点群论知识知道把最终排列结果看作是一个置换,最少操作次数是 n − 循 环 个 数 n-循环个数 n

现在考虑怎么计算 c c c的次数。

首先求交点可以直接在把结尾位置归并排序的时候一起处理。复杂度 O ( n log ⁡ n + 交 点 个 数 ) O(n\log n+交点个数) O(nlogn+)

然后所有点和正方形逆时针旋转 π 4 \frac{\pi}{4} 4π并且放大两倍,其实就是曼哈顿距离转切比雪夫距离。

然后扫描线加入和删除线段, 查询当前有没有线段覆盖这个点。
所有线段按照左端点排序,维护前缀的右端点最大值即可。

选择你喜欢的数据结构随便写一写即可。

其实也可以KD-tree,但是5e5个点看上去不是很友好,结果跑出来比扫描线还快


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;
using pii=std::pair<int,int>;
#define fi first
#define se second

cs int N=1e5+7;

struct Pnt{
	double x,y;
	Pnt(){}
	Pnt(double _x,double _y):x(_x),y(_y){}
	inline Pnt rot(){return Pnt(x-y,x+y);}
};

int A,B,C;ll ans1,ans2;
int xst,xed,n,m,tot;

Pnt p[N*5];//交点 
pii c[N];int r[N];//观测点 

namespace Scan_Line{
	struct seg{int l,r;};
	struct atom{int p;seg t;};
	atom ins[N],del[N];int tl,ct;
	int ps[N];
	namespace SGT{
		cs int N=1<<18|7;
		std::multiset<int> s[N>>1];int mx[N],M,n;
		inline void init(){
			std::sort(ps+1,ps+tl+1);
			n=std::unique(ps+1,ps+tl+1)-ps-1;
			for(M=1;M<=n+1;M<<=1);
			memset(mx,-0x3f,sizeof mx);
		}
		inline void update(int p){
			mx[p+M]=s[p].empty()?-0x3f3f3f3f:*s[p].rbegin();
			for(p+=M;p>>=1,p;)mx[p]=std::max(mx[p<<1],mx[p<<1|1]);
		}
		inline void ins(cs seg &t){
			int p=t.l,v=t.r;p=std::lower_bound(ps+1,ps+n+1,p)-ps;
			s[p].insert(v);update(p);
		}
		inline void del(cs seg &t){
			int p=t.l,v=t.r;p=std::lower_bound(ps+1,ps+n+1,p)-ps;
			s[p].erase(s[p].find(v));update(p);
		}
		inline int query(int l,int r){
			int ans=-0x3f3f3f3f;if(l>r)return ans;
			for(l+=M-1,r+=M+1;l^r^1;l>>=1,r>>=1){
				if(~l&1)ans=std::max(ans,mx[l^1]);
				if(r&1) ans=std::max(ans,mx[r^1]);
			}
			return ans;
		}
		inline bool check(cs Pnt &p){
			int r=std::upper_bound(ps+1,ps+n+1,p.y)-ps-1;
			return query(1,r)>=p.y;
		}
	}
	inline void work(){
		for(int re i=1;i<=tot;++i)p[i]=p[i].rot();
		std::sort(p+1,p+tot+1,[](cs Pnt &a,cs Pnt &b){return a.x<b.x;});
		for(int re i=1;i<=m;++i){
			++tl;ps[tl]=c[i].se-r[i];
			ins[tl]=(atom){c[i].fi-r[i],(seg){c[i].se-r[i],c[i].se+r[i]}};
			del[tl]=(atom){c[i].fi+r[i],(seg){c[i].se-r[i],c[i].se+r[i]}};
		}SGT::init();
		std::sort(ins+1,ins+tl+1,[](cs atom &a,cs atom &b){return a.p<b.p;});
		std::sort(del+1,del+tl+1,[](cs atom &a,cs atom &b){return a.p<b.p;});
		for(int re i=1,a=1,b=1;i<=tot;++i){
			while(a<=tl&&ins[a].p<=p[i].x)SGT::ins(ins[a++].t);
			while(b<=tl&&del[b].p<p[i].x)SGT::del(del[b++].t);
			if(SGT::check(p[i]))++ct;
		}
		ans1=(ll)C*ct+(ll)A*tot,ans2=(ll)C*ct+(ll)B*tot;
	}
}

inline void work(int y1,int y2,int y3,int y4){
	double t=y2+y3-y1-y4;
	double x=(y3-y1)/t*(xed-xst)+xst;
	double y=(y3-y1)/t*(y2-y1)+y1;
	p[++tot]=Pnt(x,y);
}

pii a[N],b[N];
inline void get_intersections(int l,int r){
	if(l==r)return ;int mid=l+r>>1;
	get_intersections(l,mid);
	get_intersections(mid+1,r);
	for(int re i=l,j=mid+1;i<=mid;++i){
		while(a[j].se<a[i].se&&j<=r)++j;
		for(int re k=mid+1;k<j;++k)work(a[i].fi,a[i].se,a[k].fi,a[k].se);
	}
	int i=l,j=mid+1,k=l;
	while(i<=mid&&j<=r)b[k++]=a[(a[i].se<a[j].se)?i++:j++];
	while(i<=mid)b[k++]=a[i++];
	while(j<=r)b[k++]=a[j++];
	for(int re i=l;i<=r;++i)a[i]=b[i];
}

int num[N],vis[N];
int ps[N],to[N];

signed main(){
#ifdef zxyoi
	freopen("fly.in","r",stdin);
#endif
	n=gi(),A=gi(),B=gi(),C=gi(),xst=gi(),xed=gi();
	for(int re i=1;i<=n;++i)a[i].fi=gi();
	for(int re i=1;i<=n;++i)a[i].se=num[i]=gi();
	std::sort(num+1,num+n+1);
	for(int re i=1;i<=n;++i){
		ps[i]=std::lower_bound(num+1,num+n+1,a[i].se)-num;
		to[ps[i]]=i;
	}
	get_intersections(1,n);m=gi();
	for(int re i=1;i<=m;++i){
		int x=gi(),y=gi();r[i]=gi();
		c[i]=pii(x-y,x+y);
	}
	Scan_Line::work();int ct=n;
	for(int re i=1;i<=n;++i)if(!vis[i]){--ct;while(!vis[i]){vis[i]=true;i=to[i];}}
	ans2+=(ll)ct*(A-B);
	if(ans1>ans2)std::swap(ans1,ans2);
	cout<<ans1<<" "<<ans2<<"\n";
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值