• 第十六届东北地区大学生程序设计竞赛MSpiral


    题意,你从原点出发,想走出一个k边形,一开始你向(1,0)方向走,走长度为1的直线。

     每走一步,你都会转弯,转的角度是$\frac{2\pi}{k}$,也就是每条边的角度都是$k$边形的一条边。

    然后每走$k/2$下取整步,每步走的长度就会+1。问走了$n$米之后,点的坐标是多少。

    赛场被喂了假思路往错误的方向想了好久,但是事实上题解做法十分简单。

    在赛上,队友给的思路是,有些点在一条直线上,然后找规律。事实上真有规律。

    偶数的时候模$k$相同的点在同一条直线上,且每次往外走相同的长度。

    奇数的时候模$k*(k/2)$相同的点在同一条直线上,且每次往外走相同的长度。

    然后发现不好做,因为奇数的时候,就算第一圈的长度也有$k^2$级别的,没法很快算出每个点坐标。

    之后看了题解,发现利用复数,可以飞快算出每个点坐标。

    复数的作用类似旋转矩阵,可以用k次单位根$w_k^{(1)}$乘上一个复数表示旋转。且复数的性质跟实数基本相同。

    我们用复数表示向量,第i个顶点的位置就是$e+ew+ew^2+...+2ew^d+...+kew^{i-1},e=1+i$

    借用题解中的图更加直观。

    假设一共有$(s + 1)$行,最后一行有$(t + 1)$个元素

    把这个矩阵分成三部分:除去第$(s + 1)$行的剩下矩阵,第$(s + 1)$行的前面$t$个,第$(s + 1)$行的最后一个。

    满足$n=(1 + s) * s / 2 * d + t * (s + 1) + x$

    $s, t, x$都十分好算,用求根公式可以算出s,再搞搞可以搞出$t$跟$x$。

    然后主要问题就是算整个式子的和。

    先求第一部分的和:

    $$T_1 = \sum_{i=1}^{d}\omega^{i-1}\sum_{j=1}^{s}j\omega^{d(j-1)}$$

    前后两部分是独立的,第一个求和号用等比数列求和式,第二个求和号用高中的做差方法:

    $$S = \sum_{j=1}^{s}j\omega^{d(j-1)}$$
    $$\omega S = \sum_{j=2}^{s}j\omega^{d(j-1)}$$

    相差之后,前面用等比求和,再减去最后一项,得到

    $$S = \frac{s\omega^{ds}(\omega^d-1)-\omega^{ds}+1}{(\omega^d-1)^2}$$

    $$T_1 = \frac{\omega^d-1}{\omega-1}\frac{s\omega^{ds}(\omega^d-1)-\omega^{ds}+1}{(\omega^d-1)^2}$$

    第二部分:

    $$T_2 = (s + 1)\omega^{sd}\sum_{i=1}^t\omega^{i-1}$$

    是更简单的等比数列

    第三部分只有一项:

    $$T_3 = x\omega^{sd+t}$$

    每一项都可以用快速幂求出来,然后加起来就行了。

    时间复杂度$O(Tlogn)$

    //
    // Created by onglu on 2022/5/26.
    //
    
    #include <bits/stdc++.h>
    #define double long double
    #define cpab const Point &a, const Point &b
    #define all(a) a.begin(),a.end()
    #define rall(a) a.rbegin(),a.rend()
    
    #define endl '\n'
    #define lson (rt << 1)
    #define rson (rt << 1 | 1)
    #define Mid ((l + r) / 2)
    #define int long long
    using namespace std;
    const int N = 2e6 + 1009;
    //const int N = 2e5 + 1009;
    //const int N = 5009;
    //const int N = 309;
    const double eps = 1e-9;
    const double Pi = acos(-1.0);
    struct Point {
        double x, y;
        Point() {}
        Point(double x, double y) : x(x), y(y) {}
    };
    istream &operator>>(istream& in, Point &p) {
        in >> p.x >> p.y;
        return in;
    }
    ostream &operator<<(ostream& out, Point &p) {
        out << "(" << p.x << ", " << p.y << ")";
        return out;
    }
    Point operator+(cpab) { return {a.x + b.x, a.y + b.y}; }
    Point operator-(cpab) { return {a.x - b.x, a.y - b.y}; }
    Point operator+(const Point &a, const double &b) { return {a.x + b, a.y}; }
    Point operator-(const Point &a, const double &b) { return {a.x - b, a.y}; }
    Point operator*(const Point &a, const double &b) { return {a.x * b, a.y * b}; }
    Point operator*(const double &b, const Point &a) { return {a.x * b, a.y * b}; }
    Point operator/(const Point &a, const double &b) { return {a.x / b, a.y / b}; }
    Point operator*(cpab) { return {a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x}; }
    Point operator/(cpab) { return a * Point(b.x, -b.y) / (b.x * b.x + b.y * b.y); }
    
    
    int n = 5, k = 7, a[N];
    Point Pow(Point a, int p) {
        Point ans(1, 0);
        for( ; p; p >>= 1, a = a * a)
            if(p & 1)
                ans = ans * a;
        return ans;
    }
    void work() {
        cin >> n >> k;
        int d = k / 2;
        int s = (1 + sqrt(1 + 8 * n / d)) / 2;
        if(s * (s + 1) / 2 * d > n) s -= 1;
        if(s * (s + 1) / 2 * d < n && (s + 2) * (s + 1) / 2 * d <= n) s -= 1;
        int t = (n - s * (s + 1) / 2 * d) / (s + 1);
        int x = (n - s * (s + 1) / 2 * d) % (s + 1);
        Point w(cos(2 * Pi / k), sin(2 * Pi / k));
        Point wsd = Pow(w, s * d);
        Point wt = Pow(w, t), wsdt = wsd * wt;
        Point ans = (s * wsd * (Pow(w, d) - 1) - wsd + 1) / (w - 1) / (Pow(w, d) - 1);
        ans = ans + (s + 1) * wsd / (w - 1) * (wt - 1);
        ans = ans + x * wsdt;
        cout << (ans.x + ans.y) << endl;
    }
    
    signed main() {
    #ifdef LOCAL
        freopen("C:\\Users\\onglu\\CLionProjects\\acm\\data.in", "r", stdin);
        freopen("C:\\Users\\onglu\\CLionProjects\\acm\\data.out", "w", stdout);
    #endif
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout << fixed << setprecision(10);
        int Case = 1;
       cin >> Case;
        while(Case--) work();
        return 0;
    }
    View Code
  • 相关阅读:
    Python学习笔记(十一)
    Python学习笔记(十)
    Python学习笔记(九)
    Fatal error in launcher:Unable to create process using '"'
    通过xrdp服务实现windows远程桌面连接树莓派
    Python学习笔记(七)
    Python 杂集
    Python入门小练习
    Python标准库
    [Chrome](CSS) 解决Chrome font-size 小于 12px 无效
  • 原文地址:https://www.cnblogs.com/onglublog/p/16318730.html
Copyright © 2020-2023  润新知