loj #3085 「GXOI / GZOI2019」特技飞行 扫描线+树状数组+计算几何

29 篇文章 0 订阅
16 篇文章 0 订阅

Description


太长了自己看。。。

Solution


强行题套题,真·GDOI模拟

首先可以发现A和B操作都不会影响交点的位置,那么C的贡献就是固定的了。这个可以先求出交点然后转换坐标系二维数点,离线拆分扫描线+树状数组就行了。因为有可能是实数所以离散不太好写

考虑什么时候能交换就交换。注意到一次相交意味着二者在最后会交换顺序,因此每个交点都做一次A恰好能满足初始相对顺序,且在A>B的时候是最优的。而当A<B的时候,这样算出来的一定会是较小的答案。

当A<B的时候,我们把每个航线向自己的目的位置连边,那么全部满足正确相对位置的交换次数恰好就是n-连通块数量。也就是我们至少要做这么多次A,剩余的都可以拿来做B。

然后就做完了。细节比较多

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>

#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define lb(x) (x&-x)

typedef long double ld;
const int N=2000005;
const ld eps=1e-9;

struct pos {
	ld x,y;
} p[N],jiao[N];

struct Q {
	ld x;
	int y1,y2,xs;
} q[N];

int r[N],id[N],s[N],n,cb,cj,cq;
ld b[N];

bool vis[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):v,ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void add(int x,int v) {
	for (;x<=cb+5;x+=lb(x)) s[x]+=v;
}

int get(int x) {
	int res=0;
	for (;x;x-=lb(x)) res+=s[x];
	return res;
}

pos inte(pos a,pos b,pos c,pos d) {
	ld a1=b.y-a.y,b1=a.x-b.x;
	ld c1=a1*a.x+b1*a.y;
	ld a2=d.y-c.y,b2=c.x-d.x;
	ld c2=a2*c.x+b2*c.y;
	ld cross=a1*b2-a2*b1;
	if (cross==0) return {-1,-1};
	else return {(b2*c1-b1*c2)/cross,(a1*c2-a2*c1)/cross};
}

bool cmp1(int x,int y) {
	return p[x+n].y<p[y+n].y;
}

bool cmp2(Q x,Q y) {
	if (fabs(x.x-y.x)>eps) return x.x<y.x;
	if (x.xs!=y.xs) return x.xs>y.xs;
	return false;
}

int main(void) {
	freopen("data.in","r",stdin);
	n=read(); int A=read(),B=read(),C=read();
	int sx=read(),ex=read();
	rep(i,1,n) p[i].x=sx,p[i].y=read();
	rep(i,1,n) p[i+n].x=ex,p[i+n].y=read();
	rep(i,1,n) id[i]=i;
	std:: sort(id+1,id+n+1,cmp1);
	rep(i,1,n) r[id[i]]=i;
	int cnt=n;
	rep(i,1,n) if (!vis[i]) {
		cnt--; int x=r[i];
		while (x!=i) vis[x]=1,x=r[x];
	}
	rep(i,1,n) id[i]=i;
	rep(i,1,n) rep(j,i,n*2) if (i==r[j]) {
		int aj=id[j];
		drp(k,j-1,i) {
			pos res=inte(p[id[k]],p[id[k]+n],p[aj],p[aj+n]);
			jiao[++cj]=(pos) {res.x+res.y,res.x-res.y};
			std::swap(r[k],r[k+1]);
			id[k+1]=id[k];
		} break;
	}
	int m=read();
	rep(i,1,m) {
		int tx=read(),ty=read(),c=read();
		int x=tx+ty,y=tx-ty;
		q[++cq]=(Q) {(ld)x-c,y-c,y+c,1}; b[cq]=y-c;
		q[++cq]=(Q) {(ld)x+c,y-c,y+c,-1}; b[cq]=y+c;
	}
	cb=cq;
	rep(i,1,cj) b[++cb]=jiao[i].y;
	std:: sort(b+1,b+cb+1);
	int tmp=cb; cb=1;
	rep(i,2,tmp) if (fabs(b[i]-b[i-1])>eps) b[++cb]=b[i];
	rep(i,1,cq) {
		q[i].y1=std:: lower_bound(b+1,b+cb+1,q[i].y1)-b;
		q[i].y2=std:: lower_bound(b+1,b+cb+1,q[i].y2)-b;
	}
	rep(i,1,cj) {
		int y=std:: lower_bound(b+1,b+cb+1,jiao[i].y)-b;
		q[++cq]=(Q) {jiao[i].x,y};
	}
	std:: sort(q+1,q+cq+1,cmp2);
	int wjp=0;
	rep(i,1,cq) {
		if (q[i].xs) {
			add(q[i].y1,q[i].xs);
			add(q[i].y2+1,-q[i].xs);
		} else wjp+=(get(q[i].y1)!=0);
	}
	int mxs=A*cj+wjp*C;
	int mns=A*cnt+B*(cj-cnt)+wjp*C;
	if (mxs<mns) std:: swap(mxs,mns);
	printf("%d %d\n", mns,mxs);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值