1. 程式人生 > >題解-GXOI/GZOI2019 特技飛行

題解-GXOI/GZOI2019 特技飛行

狀態 ++i ins cross inline 定位 fab 正方 pro

Problem

loj3085 bzoj不放題面差評

題意概要:給出兩條豎直直線,再給出 \(n\) 架飛機的初始航線:一條接通這兩條直線的線段,保證航線交點不在兩條直線上。現要求安排所有飛機在航線相交處做特技:

  • 擦身而過:兩架飛機按原方向線路繼續前進,一次得分 \(b\)
  • 對向交換:兩架飛機交換線路繼續前進,一次得分 \(a\)

另外,給定 \(k\) 個邊界與坐標軸成 \(45°\)角 的正方形,若一次特技被至少一個正方形囊括,則總得分加 \(c\)

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

同時要求決策方案中所有飛機 在兩條豎直直線處 按縱坐標的排序 相同

\(n, Q\leq 10^5\)

交點個數 \(\leq 5\times 10^5\)

這題意概要好像和原題面差不多了qwq

Solution

\(\mathrm{GXOI/GZOI2019}\) 的題好毒瘤啊,兩道聯賽、兩道原題、一道麻將題……完全不想打,就這題還有點意思……還偏偏要整個二合一……

交點個數 \(\leq 5\times 10^5\),應該是暗示要暴力求出來:交點一定是右部直線排序的逆序對(左部已經有序),用個 \(set\) 暴力掃就是 \(O(n\log 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\rightarrow s_2\rightarrow ...\rightarrow s_m\rightarrow s_1\)

的循環,則可以使用 \(m-1\) 次交換使得方案合法(一次交換可以使得一架且最多一架飛機到達指定位置,使 \(m-1\) 架飛機合法後剩下的那一架飛機也即合法)

那麽需要交換的次數為 "\(n-\)循環的個數"

復雜度為 \(O(n\log n+k\log 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\n",ans1 + exa, ans2 + exa);
    return 0;
}

題解-GXOI/GZOI2019 特技飛行