【扫描线+置换结论】LOJ3085「GXOI / GZOI2019」特技飞行

【题目】
LOJ
n n n架飞机从起点飞到终点,对地速度相同。每当两架飞机飞到同一点,可以选择交换飞行路线(对向交换)或继续原路线(擦肩而过),分别有 a a a b b b的得分。另外有 k k k个观测点 ( p i , q i ) (p_i,q_i) (pi,qi),可以观测到曼哈顿距离它不超过 r i r_i ri的区域。若两架飞机飞到同一点时被至少一个观测点观测到,则会额外获得 c c c的得分。
现在要求到达终点时飞机相对顺序不变,求最大可能得分和最小可能得分。
n , k ≤ 1 0 5 n,k\leq 10^5 n,k105,交点数 ≤ 5 × 1 0 5 \leq 5\times 10^5 5×105

【解题思路】
首先 c c c是可以单独拿出来计算的,这个我们后面再说。

对于 a , b a,b a,b,我们可以观察到若一直对向交换显然是合法的,那这一定是一个最值,另一个最值要求我们最小化对向交换的次数,即最大化擦肩而过的次数。

考虑若全部擦肩而过,则我们实际上可以得到若干个置换,而每个置换一定是独立的。那么对于每个大小为 x x x的置换来说,实际上其交换的下界就是 x − 1 x-1 x1,这个可以考虑每个交点相当于创造一个逆序对吧,就是要将逆序对全部交换回来。于是对向交换的最小次数就是 n − n- n置换个数。

于是 a , b a,b a,b的贡献看起来很难,反而可以 O ( n ) O(n) O(n)算了。

对于 c c c,实际上就是数有多少个交点被包含在矩形中。首先求交点的话实际上就是每个逆序对,我们随便用一个 set \text{set} set之类的维护然后按顺序插入数字就可以了。然后曼哈顿距离还要拆绝对值来算,我们转成切比雪夫距离,然后就可以直接扫描线做了。

这部分的复杂度大概是 O ( k log ⁡ n ) O(k\log n) O(klogn)的,但常数极大,至少我们被KD树做法吊打。

【参考代码】

#include<bits/stdc++.h>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;

typedef pair<int,int> pii;
typedef double db;//use long double will be slower
const int N=1e5+10,M=N*5;

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

namespace Data_Structure
{
	int cnt;
	struct line
	{
		int x,yl,yr,op;
		line(int _x=0,int _l=0,int _r=0,int _o=0):x(_x),yl(_l),yr(_r),op(_o){}
		bool operator <(const line&rhs)const{return x==rhs.x?op>rhs.op:x<rhs.x;}
	}li[M<<1];
	struct Segment
	{
		#define ls (x<<1)
		#define rs (x<<1|1)
		int tag[M*6];
		void update(int x,int l,int r,int L,int R,int v)
		{
			if(L<=l && r<=R) {tag[x]+=v;return;}
			int mid=(l+r)>>1;
			if(L<=mid) update(ls,l,mid,L,R,v);
			if(R>mid) update(rs,mid+1,r,L,R,v);
		}
		int query(int x,int l,int r,int p)
		{
			if(l==r) return tag[x];
			int mid=(l+r)>>1;
			if(p<=mid) return tag[x]+query(ls,l,mid,p);
			else return tag[x]+query(rs,mid+1,r,p);
		}
		#undef ls
		#undef rs
	}tr;
}
using namespace Data_Structure;

namespace DreamLolita
{
	int n,A,B,C,S,T,K,cro,ans1,ans2,MI,MX;
	int R[N],be[N],ed[N],vis[N];
	db b[N],k[N];
	set<pii>st;
	struct point
	{
		db x,y;
		point(db _x=0,db _y=0):x(_x),y(_y){}
	}p[M],vs[N];
	void addcross(int i,int j)
	{
		//printf("getcross:%d %d\n",i,j);
		db x=(b[j]-b[i])/(k[i]-k[j]),y=k[i]*x+b[i];
		p[++cro]=point(x+y,x-y);
	}
	void getcross()
	{
		for(int i=1;i<=n;++i) k[i]=(1.0*(ed[i]-be[i]))/(1.0*(T-S)),b[i]=1.0*be[i]-k[i]*S;
		for(int i=1;i<=n;++i) 
		{
			for(auto it=st.lower_bound(mkp(ed[i],i));it!=st.end();++it) addcross(i,(*it).se);
			st.insert(mkp(ed[i],i));
		}
		//printf("cross:%d\n",cro);
		//for(int i=1;i<=cro;++i) printf("%Lf %Lf\n",p[i].x,p[i].y);
	}

	pair<int,int>a[N];
	void dfs(int x){vis[x]=1;if(!vis[a[x].se])dfs(a[x].se);}
	void calcAB()
	{
		for(int i=1;i<=n;++i) a[i]=mkp(ed[i],i);
		sort(a+1,a+n+1);
		for(int i=1;i<=n;++i) if(!vis[i]) ++ans1,dfs(i);
		MI=A*cro;MX=(n-ans1)*A+(cro-(n-ans1))*B;
		if(MI>MX) swap(MI,MX);
		//printf("MI:%d MX:%d\n",MI,MX);
	}

	int cntx,cnty;
	db px[M<<1],py[M<<1];
	void calcC()
	{
		for(int i=1;i<=K;++i) px[++cntx]=vs[i].x-R[i],px[++cntx]=vs[i].x+R[i],py[++cnty]=vs[i].y-R[i],py[++cnty]=vs[i].y+R[i];
		for(int i=1;i<=cro;++i) px[++cntx]=p[i].x,py[++cnty]=p[i].y;
		sort(px+1,px+cntx+1);cntx=unique(px+1,px+cntx+1)-px-1;
		sort(py+1,py+cnty+1);cnty=unique(py+1,py+cnty+1)-py-1;
		for(int i=1;i<=K;++i) 
		{
			int xl=lower_bound(px+1,px+cntx+1,vs[i].x-R[i])-px,xr=lower_bound(px+1,px+cntx+1,vs[i].x+R[i])-px;
			int yl=lower_bound(py+1,py+cnty+1,vs[i].y-R[i])-py,yr=lower_bound(py+1,py+cnty+1,vs[i].y+R[i])-py;
			li[++cnt]=line(xl,yl,yr,1);li[++cnt]=line(xr,yl,yr,-1);
		}
		for(int i=1;i<=cro;++i) 
		{
			int x=lower_bound(px+1,px+cntx+1,p[i].x)-px,y=lower_bound(py+1,py+cnty+1,p[i].y)-py;
			li[++cnt]=line(x,y,0,0);
		}
		sort(li+1,li+cnt+1);
		//puts("lines:");
		//for(int i=1;i<=cnt;++i) printf("%d %d %d %d\n",li[i].x,li[i].yl,li[i].yr,li[i].op);
		for(int i=1,j,r;i<=cnt;i=j+1)
		{
			j=i-1;r=j+1;
			while(li[j+1].x==li[i].x && li[j+1].op==1) ++j;
			for(int l=r;l<=j;++l) tr.update(1,1,cnty,li[l].yl,li[l].yr,1);

			r=j+1;
			while(li[j+1].x==li[i].x && li[j+1].op==0) ++j;
			for(int l=r;l<=j;++l) ans2+=(tr.query(1,1,cnty,li[l].yl)>0?1:0);

			r=j+1;
			while(li[j+1].x==li[i].x && li[j+1].op==-1) ++j;
			for(int l=r;l<=j;++l) tr.update(1,1,cnty,li[l].yl,li[l].yr,-1);
		}
		MI+=ans2*C;MX+=ans2*C;
	}

	void solution()
	{
		n=read();A=read();B=read();C=read();S=read();T=read();
		for(int i=1;i<=n;++i) be[i]=read();
		for(int i=1;i<=n;++i) ed[i]=read();
		K=read();
		for(int i=1;i<=K;++i) 
		{
			int x=read(),y=read();R[i]=read();
			vs[i].x=x+y;vs[i].y=x-y;
		}
		getcross();calcAB();calcC();
		printf("%d %d\n",MI,MX);
	}
}

int main()
{
#ifdef Durant_Lee
	freopen("LOJ3085.in","r",stdin);
	freopen("LOJ3085.out","w",stdout);
#endif
	DreamLolita::solution();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值