loj3085 bzoj不放题面差评c++
题意概要:给出两条竖直直线,再给出 \(n\) 架飞机的初始航线:一条接通这两条直线的线段,保证航线交点不在两条直线上。现要求安排全部飞机在航线相交处作特技:git
另外,给定 \(k\) 个边界与坐标轴成 \(45°\)角 的正方形,若一次特技被至少一个正方形囊括,则总得分加 \(c\)spa
现要求决策每次相遇作的特技,求最大/最小收益code
同时要求决策方案中全部飞机 在两条竖直直线处 按纵坐标的排序 相同排序
\(n, Q\leq 10^5\)get
交点个数 \(\leq 5\times 10^5\)input
这题意概要好像和原题面差很少了qwqit
\(\mathrm{GXOI/GZOI2019}\) 的题好毒瘤啊,两道联赛、两道原题、一道麻将题……彻底不想打,就这题还有点意思……还恰恰要整个二合一……io
交点个数 \(\leq 5\times 10^5\),应该是暗示要暴力求出来:交点必定是右部直线排序的逆序对(左部已经有序),用个 \(set\) 暴力扫就是 \(O(n\log n)\)。就获得了全部交点class
\(c\) 的贡献很明显是单独求的,将坐标系旋转 \(45°\) 后就能够扫描线了。因此其实难度在于如何求 \(a,b\) 的贡献
设交点总数为 \(t\),其中有 \(x\) 个点交换航线,\(t-x\) 个点不交换。这部分贡献是 \(ax+b(t-x)=(a-b)x+bt\),因此总得分的最大最小值必定是 \(x\) 取最值时取得,即只须要求最少/多有多少个点交换航线
首先由于要求飞机在起终点的顺序不变,而交换航线不会改变顺序,全部点都交换航线确定是可行的,即 \(x_{\max}=t\)
再考虑最小值。假如全部点都不换航线(\(x = 0\)),在大部分状况下都不合法,考虑使用最少的交换航线使得最终状态与初始状态一致:能够发现,若按照原航线行进时,飞机状态的变化产生了 \(s_1\rightarrow s_2\rightarrow ...\rightarrow s_m\rightarrow s_1\) 的循环,则能够使用 \(m-1\) 次交换使得方案合法(一次交换能够使得一架且最多一架飞机到达指定位置,使 \(m-1\) 架飞机合法后剩下的那一架飞机也即合法)
那么须要交换的次数为 "\(n-\)循环的个数"
复杂度为 \(O(n\log n+k\log k)\),瓶颈在暴力找交点和扫描线
代码中 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\n",ans1 + exa, ans2 + exa); return 0; }