• 「清华集训 2017」我的生命已如风中残烛


    题目链接

    问题分析

    题意不难理解。只是为什么绳子固定的那个端点不算一个钉子

    然后没有什么想法,于是就考虑暴力。不难发现,每次找到一个圈之后,长度就会剩下不到一半。于是至多找(log L)次圈。每次找圈可能找到(O(n))个点,找下一个点需要(O(n))的时间。于是得到一个虚假的总时间复杂度是(O(Tmlog L n ^2 ))(3e10)感觉(10)秒不太行。

    然后考虑次数最多的操作进行优化,就是加速找下一个点。首先预处理以每个点为中心的极角排序,之后先二分查找极角再扫下一个点,这样能大大加速找点的时间。虽然时间复杂度上限应该没有变,但是不难发现这个上限已经很松了。然后就勇一发。然后总时间只有1700+ms?单个点最慢只有400+ms?这就没了?

    下面是优化建议,本人没有实现过:经实测上述暴力加上第二点优化总时间在1500+ms

    1、开始的时候直接预处理点(x),极角为(alpha),正好碰到点(y)的长度。这样开始时一个很松的(O(n^3))预处理,然后后面每次查找只需要(O(log n))即可。这样时间复杂度为(O(Tn^3+Tmlog L n log n))。大概在(2e9)。上限比直接暴力大概能少一个数量级。

    2、对于经常用到的长度、距离函数开数组存储,属于常数优化。

    参考程序

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    void Main();
    int main() {
        int TestCases;
        scanf("%d", &TestCases);
        for (; TestCases--;) Main();
        return 0;
    }
    
    const int Maxn = 510;
    const double Eps = 1e-12;
    inline int Cmp(const double x, const double y) {
        if (fabs(x - y) <= Eps)
            return 0;
        if (x - y > Eps)
            return 1;
        return -1;
    }
    struct point {
        double x, y;
        point() {}
        point(const double _x, const double _y) : x(_x), y(_y) {}
        inline void Read() {
            scanf("%lf%lf", &x, &y);
            return;
        }
        inline point operator-(const point Other) const { return point(x - Other.x, y - Other.y); }
        inline double operator*(const point Other) const { return x * Other.y - Other.x * y; }
        inline point operator*(const double Other) const { return point(x * Other, y * Other); }
        inline double Len() const { return sqrt(x * x + y * y); }
    };
    struct member {
        int Quadrant, Index;
        point Point;
        member() {}
        member(const int _Index, const point _Point) {
            Index = _Index;
            Point = _Point;
            Quadrant = (Cmp(Point.y, 0.0) != 0) ? Cmp(Point.y, 0.0) : Cmp(Point.x, 0.0);
            return;
        }
        inline bool operator<(const member Other) const {
            if (Quadrant != Other.Quadrant)
                return Quadrant < Other.Quadrant;
            double Temp = Cmp(Point * Other.Point, 0.0);
            return Temp < 0 || Temp == 0 && Point.Len() > Other.Point.Len();
        }
    };
    int n, m;
    point Point[Maxn];
    member Relation[Maxn][Maxn];
    int RelationSize[Maxn];
    point Start, Direction;
    double Length;
    int Stack[Maxn], Vis[Maxn][Maxn];
    
    void Init();
    int Work();
    void Main() {
        Init();
        for (int i = 1; i <= m; ++i) printf("%d
    ", Work());
        return;
    }
    
    void Init() {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; ++i) Point[i].Read();
        for (int i = 1; i <= n; ++i) {
            RelationSize[i] = 0;
            for (int j = 1; j <= n; ++j)
                if (i != j)
                    Relation[i][++RelationSize[i]] = member(j, Point[j] - Point[i]);
            sort(Relation[i] + 1, Relation[i] + RelationSize[i] + 1);
        }
        return;
    }
    
    int Find(const int Index, const point Direction);
    int Work() {
        Start.Read();
        Direction.Read();
        scanf("%lf", &Length);
        Direction = Direction - Start;
        Direction = Direction * (Length / Direction.Len());
    
        for (int i = 1; i <= n; ++i) Relation[0][i] = member(i, Point[i] - Start);
        RelationSize[0] = n;
        sort(Relation[0] + 1, Relation[0] + n + 1);
    
        int First = Find(0, Direction);
        if (First == -1)
            return 1;
        Length -= Relation[0][First].Point.Len();
        Direction = Relation[0][First].Point * (Length / Relation[0][First].Point.Len());
        First = Relation[0][First].Index;
    
        int Second = Find(First, Direction);
        if (Second == -1)
            return 2;
        Length -= Relation[First][Second].Point.Len();
        Direction = Relation[First][Second].Point * (Length / Relation[First][Second].Point.Len());
        Second = Relation[First][Second].Index;
    
        int Ans = 2;
        for (int Time = 1;; ++Time) {
            Stack[0] = 0;
            Stack[++Stack[0]] = First;
            Stack[++Stack[0]] = Second;
    
            for (;;) {
                int Next = Find(Second, Direction);
                if (Next == -1)
                    return Ans + Stack[0] - 1;
                Length -= Relation[Second][Next].Point.Len();
                Direction = Relation[Second][Next].Point * (Length / Relation[Second][Next].Point.Len());
                Next = Relation[Second][Next].Index;
                Stack[++Stack[0]] = Next;
                First = Second;
                Second = Next;
    
                if (Vis[First][Second] == Time)
                    break;
                Vis[First][Second] = Time;
            }
            --Stack[0];
    
            int Pos;
            for (Pos = 0; Pos < Stack[0]; ++Pos)
                if (Stack[Pos] == First && Stack[Pos + 1] == Second)
                    break;
            double TotalLength = 0;
            for (int i = Pos; i < Stack[0]; ++i) TotalLength += (Point[Stack[i + 1]] - Point[Stack[i]]).Len();
    
            Ans += Stack[0] - 1;
            int Times = (int)floor(Length / TotalLength);
            Ans += Times * (Stack[0] - Pos);
            Length -= TotalLength * Times;
            Direction = Direction * (Length / Direction.Len());
        }
        return Ans;
    }
    
    int Find(const int Index, const point Dir) {
        member Direction = member(0, Dir);
        int Size = RelationSize[Index];
        int Ans = lower_bound(Relation[Index] + 1, Relation[Index] + Size + 1, Direction) - Relation[Index];
        if (Ans > Size)
            Ans = 1;
        for (int i = 1; i <= Size; ++i) {
            if (Cmp(Dir.Len(), Relation[Index][Ans].Point.Len()) == 1)
                return Ans;
            ++Ans;
            if (Ans > Size)
                Ans = 1;
        }
        return -1;
    }
    
  • 相关阅读:
    UVALive 3645 Objective: Berlin(最大流 :时序模型)
    【】筛选素数法
    UVaLive 7361(矩阵快速幂)
    【模板】KMP字符串匹配【KMP】
    【模板】KMP字符串匹配【KMP】
    【模板】KMP字符串匹配【KMP】
    八百标兵奔北坡【DP】
    八百标兵奔北坡【DP】
    八百标兵奔北坡【DP】
    八百标兵奔北坡【DP】
  • 原文地址:https://www.cnblogs.com/chy-2003/p/11297535.html
Copyright © 2020-2023  润新知