• 题解-GXOI/GZOI2019 特技飞行


    Problem

    loj3085 bzoj不放题面差评

    题意概要:给出两条竖直直线,再给出 (n) 架飞机的初始航线:一条接通这两条直线的线段,保证航线交点不在两条直线上。现要求安排所有飞机在航线相交处做特技:

    • 擦身而过:两架飞机按原方向线路继续前进,一次得分 (b)
    • 对向交换:两架飞机交换线路继续前进,一次得分 (a)

    另外,给定 (k) 个边界与坐标轴成 (45°)角 的正方形,若一次特技被至少一个正方形囊括,则总得分加 (c)

    现要求决策每次相遇做的特技,求最大/最小收益

    同时要求决策方案中所有飞机 在两条竖直直线处 按纵坐标的排序 相同

    (n, Qleq 10^5)

    交点个数 (leq 5 imes 10^5)

    这题意概要好像和原题面差不多了qwq

    Solution

    (mathrm{GXOI/GZOI2019}) 的题好毒瘤啊,两道联赛、两道原题、一道麻将题……完全不想打,就这题还有点意思……还偏偏要整个二合一……

    交点个数 (leq 5 imes 10^5),应该是暗示要暴力求出来:交点一定是右部直线排序的逆序对(左部已经有序),用个 (set) 暴力扫就是 (O(nlog n))。就得到了所有交点

    (c) 的贡献很明显是单独求的,将坐标系旋转 (45°) 后就可以扫描线了。所以其实难度在于如何求 (a,b) 的贡献

    设交点总数为 (t),其中有 (x) 个点交换航线,(t-x) 个点不交换。这部分贡献是 (ax+b(t-x)=(a-b)x+bt),所以总得分的最大最小值一定是 (x) 取最值时取得,即只需要求最少/多有多少个点交换航线

    首先因为要求飞机在起终点的顺序不变,而交换航线不会改变顺序,所有点都交换航线肯定是可行的,即 (x_{max}=t)

    再考虑最小值。假如所有点都不换航线((x = 0)),在大部分情况下都不合法,考虑使用最少的交换航线使得最终状态与初始状态一致:可以发现,若按照原航线行进时,飞机状态的变化产生了 (s_1 ightarrow s_2 ightarrow ... ightarrow s_m ightarrow s_1) 的循环,则可以使用 (m-1) 次交换使得方案合法(一次交换可以使得一架且最多一架飞机到达指定位置,使 (m-1) 架飞机合法后剩下的那一架飞机也即合法)

    那么需要交换的次数为 "(n-)循环的个数"

    复杂度为 (O(nlog n+klog k)),瓶颈在暴力找交点和扫描线

    Code

    代码中 get_cross 为暴力找交点,Circle 为找循环,Extra 为扫描线

    #include <bits/stdc++.h>
    using namespace std;
    
    inline void read(int&x){
    	char c11=getchar();x=0;while(!isdigit(c11))c11=getchar();
    	while(isdigit(c11))x=x*10+c11-'0',c11=getchar();
    }
    
    const double eps = 1e-6;
    const int N = 101000, M = 501000;
    
    int l0[N], r0[N];
    int n,L,R;
    
    struct pnt {
    	double x, y;
    	friend inline bool operator < (const pnt&A,const pnt&B) {return A.x < B.x;}
    }p[M];
    
    int p0;
    
    pnt crs(int i,int j) {
    	double th = (double)abs(l0[i] - l0[j]) / (abs(l0[i] - l0[j]) + abs(r0[i] - r0[j]));
    	return (pnt) {L + (R-L) * th, l0[i] + (r0[i] - l0[i]) * th};
    }
    
    set <int> c;
    set <int> :: iterator itr;
    map <int,int> mp;
    
    int get_cross() {
    	for(int i=1;i<=n;++i) {
    		c.insert(r0[i]);
    		mp[r0[i]] = i;
    		itr = c.find(r0[i]);
    		for(++itr; itr != c.end(); ++itr) {
    			int j = mp[*itr];
    			p[++p0] = crs(i, j);
    		}
    	}
    	return p0;
    }
    
    namespace Circle {
    	int b[N], dad[N];
    	int find(int x) {return dad[x] ? dad[x] = find(dad[x]) : x;}
    	int main() {
    		for(int i=1;i<=n;++i) b[i] = r0[i];
    		sort(b+1,b+n+1);
    		int res = 0;
    		for(int i=1,j,p1,p2;i<=n;++i) {
    			j = lower_bound(b+1,b+n+1,r0[i])-b;
    			if((p1 = find(i)) == (p2 = find(j))) ++res;
    			else dad[p1] = p2;
    		}
    		return p0 - (n - res);
    	}
    }
    
    namespace Extra {
    	double b[M+N+N];
    	int Q, tot;
    	
    	struct LNE {
    		double x, y1, y2; int w;
    		friend inline bool operator < (const LNE&A,const LNE&B) {return A.x < B.x;}
    	}l[N+N];
    	
    	namespace BIT {
    		#define lb(x) (x&(-x))
    		int d[M+N+N];
    		inline int qry(int x) {
    			int res = 0;
    			for(int i=x;i;i-=lb(i)) res += d[i];
    			return res;
    		}
    		inline void upd(int l, int r, int w) {
    			for(;l<=tot;l+=lb(l)) d[l] += w;
    			for(++r;r<=tot;r+=lb(r)) d[r] -= w;
    		}
    		#undef lb
    	}
    	
    	void input() {
    		double x, y;
    		for(int i=1;i<=p0;++i) {
    			x = p[i].x, y = p[i].y;
    			p[i].x = x + y, p[i].y = x - y;
    			b[++tot] = p[i].y;
    		}
    		sort(p+1,p+p0+1);
    		int r; read(Q);
    		for(int i=1;i<=Q;++i) {
    			scanf("%lf%lf",&x,&y), read(r);
    			l[i+i-1].x = x + y - r - eps;
    			l[i+i-1].y1 = x - r - y - eps;
    			l[i+i-1].y2 = x - y + r + eps;
    			l[i+i-1].w = 1;
    			l[i+i].x = x + y + r + eps;
    			l[i+i].y1 = x - y - r - eps;
    			l[i+i].y2 = x + r - y + eps;
    			l[i+i].w = -1;
    			
    			b[++tot] = x - y + r + eps;
    			b[++tot] = x - y - r - eps;
    		}
    		Q <<= 1;
    		sort(l+1,l+Q+1);
    		
    		sort(b+1,b+tot+1);
    		int tt0 = 0; b[0] = -1e10;
    		for(int i=1;i<=tot;++i)
    			if(fabs(b[i] - b[i-1]) > eps)
    				b[++tt0] = b[i];
    		tot = tt0;
    		
    		for(int i=1;i<=p0;++i) p[i].y = lower_bound(b+1,b+tot+1,p[i].y) - b;
    		for(int i=1;i<=Q;++i) {
    			l[i].y1 = lower_bound(b+1,b+tot+1,l[i].y1) - b;
    			l[i].y2 = lower_bound(b+1,b+tot+1,l[i].y2) - b;
    		}
    	}
    	
    	int main() {
    		input();
    		
    		int res = 0;
    		for(int i=1,j=1;i<=p0;++i) {
    			while(j <= Q and l[j].x <= p[i].x)
    				BIT::upd(l[j].y1, l[j].y2, l[j].w), ++j;
    			if(BIT::qry(p[i].y)) ++res;
    		}
    		return res;
    	}
    }
    
    int main() {
    	int A, B, C;
    	read(n), read(A), read(B), read(C);
    	read(L), read(R);
    	for(int i=1;i<=n;++i) read(l0[i]);
    	for(int i=1;i<=n;++i) read(r0[i]);
    	
    	int ans1 = A * get_cross(), ans2 = ans1, exa;
    	ans2 += (B - A) * Circle::main();
    	exa = C * Extra::main();
    	if(ans1 > ans2) swap(ans1, ans2);
    	printf("%d %d
    ",ans1 + exa, ans2 + exa);
    	return 0;
    }
    
  • 相关阅读:
    Chrome Native Messaging 与本地程序之间的通信
    由于扩展配置问题而无法提供您请求的页面。如果该页面是脚本,请添加处理程序。如果应下载文件,请添加 MIME 映射。
    linux
    maven POM.xml 标签详解 转
    java-线程观察整个生命周期
    java-Web项目开发中Spring整合Junit单元测试
    java-Map集合的四种遍历方式
    Java-8内置的核心函数式接口接口
    正则表达式
    Java-Atomic系列12大类实例讲解和原理分解
  • 原文地址:https://www.cnblogs.com/penth/p/10777150.html
Copyright © 2020-2023  润新知