zoukankan      html  css  js  c++  java
  • USACO 2020 January Contest, Platinum Problem 3. Falling Portals

    题目链接:http://usaco.org/index.php?page=viewproblem2&cpid=998

    题解:挺有意思的一道题。对于可以传送的i,j必然存在一个t>=0使得A[i]-ti=A[j]-tj,移项,发现t=(A[i]-A[j])/(i-j),也就是说t是点(i,A[i])和(j,A[j])连线的斜率。并且,如果连续传送,t必须是增大的。所以直线必须是越来越斜的。我们可以想象成开始时有一块木板绕着(i, A[i])转动表示时间的流动,当木板逆时针扫到另一个点则表明,此时木板可以转换支点,也就是奶牛传送。不妨先考虑A[Q[i]]>A[i]的情况。木板开始以i为支点逆时针转动,当木板第一个碰到的就是Q[i]时直接传送为最优解。否则,木板可以碰到i的左下方的点,那么就传送到左下方。木板继续转动,直到碰到Q[i]。如何来模拟这个过程?我们发现最优解必定在i的左下方包括i,而且最后一步,必定也是在(Q[i], A[Q[i]])的左下方。也就是说对于所有j<i,j<Q[i],A[j]<A[i]的点,我们只要找到

      min( (A[j]-A[Q[i]]) / (j-Q[i])),如果找不到当然是-1.

      不难设计出一个算法。先对A[i]排序,先做A[Q[i]]>A[i]的情况,反过来是一样的。对于i下面的点做凸包。点连起来就一定会是这样的。

    显然答案也是凸的,而且是一个单峰函数,所以我们可以三分。并且我们要先在凸包中找到区间的横坐标都小于Q[i]和i的。

    代码:

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int maxn = 2e5 + 5;
    
    int N, n, ex;
    int A[maxn];
    
    struct Cow {
        int x, y, q;
        
        Cow(int x=0, int y=0)
            : x(x), y(y) {}
        
        Cow operator - (Cow b) {
            return Cow(x-b.x, y-b.y);
        }
    }cows[maxn], ch[maxn];
    
    int ansnum[maxn], ansdet[maxn];
    
    bool cmp(Cow A, Cow B) {
        return A.y < B.y;
    }
    
    long long Cross(Cow A, Cow B) {
        return (long long)A.x*B.y - (long long)A.y*B.x;
    }
    
    int gcd(int a, int b) {
        if (b == 0) return a;
        return gcd(b, a%b);
    }
    
    double F(int q, int i) {
        return (double)(A[q]-ch[i].y)/(q-ch[i].x);
    }
    
    int find(int x, int L, int R) {
        if (L > R) return -1;
        while (R-L > 2) {
            int m1 = L + (R-L+1)/3;
            int m2 = R - (R-L+1)/3;
            if (F(x, m1) >= F(x, m2)) L = m1;
            else R = m2;
        }
        if (L == R) {
            if (F(x, L) <= 0) return -1;
            return L;
        }
        if (L+1 == R) {
            if (F(x, L) <= F(x, R)) return L;
            return R;
        }
        if (F(x, L) <= F(x, L+1) && F(x, L) <= F(x, R)) return L;
        else if (F(x, L+1) <= F(x, R)) return L+1;
        return R;
    }
    
    int main() {
        freopen("falling.in", "r", stdin);
        freopen("falling.out", "w", stdout);
        scanf("%d", &N);
        for (int i = 1; i <= N; ++i) {
            scanf("%d", &A[i]);
            cows[i] = Cow(i, A[i]);
            ansdet[i] = 1;
        }
        for (int i = 1; i <= N; ++i) scanf("%d", &cows[i].q);
        memset(ansnum, -1, sizeof(ansnum));
        sort(cows + 1, cows + N + 1, cmp);
        n = 0;
        ex = 0;
        for (int i = 1; i <= N; ++i) {
            while (n > 1 && Cross(cows[i]-ch[n-2], ch[n-1]-ch[n-2]) <= 0) n--;
            if (n > 0 && ex >= n) ex = n-1;
            ch[n++] = cows[i];
            if (ch[ex].x > cows[i].x) ex = n-1;
            if (A[cows[i].q] >= cows[i].y) {
                int L = 0, R = ex, l, r;
                while (L < R) {
                    int mid = (L+R) / 2;
                    if (ch[mid].x <= min(cows[i].x, cows[i].q)) R = mid;
                    else L = mid+1;
                }
                l = L;
                L = ex; R = n-1;
                while (L < R) {
                    int mid = (L+R) / 2 + 1;
                    if (ch[mid].x <= min(cows[i].x, cows[i].q)) L = mid;
                    else R = mid-1;
                }
                r = R;
                int j = find(cows[i].q, l, r);
                if (j == -1) continue;
                ansnum[cows[i].x] = abs(A[cows[i].q]-ch[j].y);
                ansdet[cows[i].x] = abs(cows[i].q-ch[j].x);
            }
        }
        n = 0;
        ex = 0;
        for (int i = N; i >= 1; --i) {
            while (n > 1 && Cross(cows[i]-ch[n-2], ch[n-1]-ch[n-2]) <= 0) n--;
            if (n > 0 && ex >= n) ex = n-1;
            ch[n++] = cows[i];
            if (ch[ex].x < cows[i].x) ex = n-1;
            if (A[cows[i].q] <= cows[i].y) {
                int L = 0, R = ex, l, r;
                while (L < R) {
                    int mid = (L+R) / 2;
                    if (ch[mid].x >= max(cows[i].x, cows[i].q)) R = mid;
                    else L = mid+1;
                }
                l = L;
                L = ex; R = n-1;
                while (L < R) {
                    int mid = (L+R) / 2 + 1;
                    if (ch[mid].x >= max(cows[i].x, cows[i].q)) L = mid;
                    else R = mid-1;
                }
                r = R;
                int j = find(cows[i].q, l, r);
                if (j == -1) continue;
                ansnum[cows[i].x] = abs(A[cows[i].q]-ch[j].y);
                ansdet[cows[i].x] = abs(cows[i].q-ch[j].x);
            }
        }
        for (int i = 1; i <= N; ++i) {
            if (ansnum[i] == -1) printf("-1
    ");
            else {
                int g = gcd(ansnum[i], ansdet[i]);
                printf("%d/%d
    ", ansnum[i]/g, ansdet[i]/g);
            }
        }
        return 0;
    }
  • 相关阅读:
    自制的 MPlayer Skin
    mplayer filter 参数及效果
    可拖动的层DIV的完整源代码【转】
    Hibernate的检索方式(一)【转】
    HQL经典语句
    常适用的特效网页代码
    C#优化字符串操作【转】
    Hibernate的检索方式(二)【转】
    内联inline的使用方法【转】
    Hibernate的检索方式(三)【转】
  • 原文地址:https://www.cnblogs.com/albert7xie/p/12510226.html
Copyright © 2011-2022 走看看