[HackerRank]Week Of Code 23

又是玩得比较爽的一场
HackerRank Week of Code 23部分题解

Gears of War

  • 题意: N(N3) 个齿轮围成一圈问是否正常工作
  • 解法:一眼看出来奇数不行,偶数可行

Lighthouse

  • 题意:点为可放井号不可,问能放多大的那种图形,如图欧几里得距离固定的”圆”
  • 解法:二分暴力,因为边长最大50,不二分不知道能不能过(问号脸)

Treasure Hunting

  • 题意: 从(0,0)到(x,y)每次移动(a,b)格,可中途改变一次方向,问两个方向分别移动多少次
  • 解法:根据斜率乘积等于-1,推推公式就ok啦。
  • Code:
def rawInput():
    return [float(x) for x in raw_input().split()] 

x, y = rawInput()
a, b = rawInput()
c = a * x + b * y
ab = a * a + b * b
rlt1, rlt2 = c * 1.0 / ab, 1.0 * (y * ab - b * c) / (a * ab)
print rlt1
print rlt2

Unexpected Problem

  • 题意:给出一个字符串s和整数m,总有多少个串t,使得 len(t)<=ms+t=t+s
  • 解法:因为要 s+t=t+s 所以必须是 sn(n1)t 组成,所以找到最短的 s 前缀s1使得 s 可以由他组成就可以了,结果就是m/len(s1)%MOD,找这前缀的时候。暴力就可以啦,判断能不能由它组成的时候。用字符串hash可以 O(1) 判定,PS:数据不是很强,单hash也能过.
  • Code:
#include <bits/stdc++.h>

typedef long long LLONG;
typedef long long ULLONG;

const int N = 5e5 + 10;
const int MOD = 1e9 + 7;
const int MOD2 = 1e9 + 9;
const int magic = 31;

char s[N];
ULLONG hs[N];
ULLONG hs2[N];
ULLONG power[N];
ULLONG power2[N];

inline ULLONG get(int l, int r) {
    return hs[r] - hs[l] * power[r - l];
}

inline ULLONG get(int l, int r, ULLONG h[], ULLONG p[], LLONG m) {
    return ((h[r] - h[l] * p[r - l]) % m + m) % m;
}

int main() {
    LLONG n;
    scanf("%s%lld", s, &n);
    int lth = 0;
    power[0] = 1;
    power2[0] = 1;
    for (int i = 0; s[i]; ++i) {
        ++lth;
        hs[i + 1] = (hs[i] * magic + s[i] - 'a') % MOD;
        hs2[i + 1] = (hs2[i] * magic + s[i] - 'a') % MOD2;
        power[i + 1] = power[i] * magic % MOD;
        power2[i + 1] = power2[i] * magic % MOD2;
    }
    int minlth = lth;
    for (int i = 1; i <= lth; ++i) {
        if (lth % i == 0) {
            bool mark = true;
            for (int j = i; j <= lth && mark; j += i) {
                if (get(j - i, j, hs, power, MOD) != hs[i] || get(j - i, j, hs2, power2, MOD2) != hs2[i]) {
                    mark = false;
                }
            }
            if (mark) {
                minlth = i; break;
            }
        }
    }
    printf("%d\n", (n / minlth) % MOD);
    return 0;
}

Gravity Tree

  • 题意: N(N1e5) 个节点1为根节点的树。 Q(Q1e5) 次询问,每次询问 (u,v) ,求 v 形成的子树中所有结点到u的距离的平方和
  • 解法:
    ① 现有节点 abc;b c 的祖先且距离为dbca不是 b 的子节点且距离为daba c 节点的距离平方等于(dbc+dab)2=d2bc+2dabdbc+d2ab,由公式可以看出,我们需要维护 b 形成的子树的d2d,到 a 的距离平方和可以在O(1)时间内算出.
    ②现在如果 a 也是b的子节点的时候,我们把整棵树转一下使得 a 是根节点算出所有节点(N) a 节点需要维护的信息,这时候结果就是以a为根节点时所有节点到 a 的距离平方和减去此时(a为根节点) b 的子树(不包含a)到 a 的距离平方和(以b为根节点时所有节点到 b 的平方和减去正常树时有a的子树到 b 的平方和)
    ③这些信息都可以事先预处理出来,所以回答是O(1)
    ④计算两个节点的距离用LCA
  • Code:
#pragma comment(linker, "/STACK:102400000,102400000")
#include <bits/stdc++.h>

typedef long long LLONG;

const int N = 1e5 + 10;
const int MOD = 1e9 + 7;
const int K = 20;

int depth[N];
LLONG d2[N], d[N], rd2[N], rd[N];
int son[N], parent[N];
PII edge[N];
int head[N], etot;
int up[N][K];

inline void addEdge(int u, int v) {
    edge[etot] = std::make_pair(v, head[u]);
    head[u] = etot++;
}

inline void assign(int u) {
    int f = parent[u];
    depth[u] = depth[f] + 1;
    up[u][0] = f; son[u] = 1;
    d[u] = 0; d2[u] = 0;
}

inline void compute(int u, int v) {
    son[u] += son[v];
    d2[u] += d2[v] + 2 * d[v] + son[v];
    d[u] += d[v] + son[v];
}

inline void dfs(int u) {
    assign(u);
    for (int e = head[u]; ~e; e = edge[e].second) {
        int v = edge[e].first;
        dfs(v);
        compute(u, v);
    }
}

inline std::tuple<LLONG, LLONG, LLONG>compute(int n, int u, int v) {
    return std::make_tuple(n - son[v], rd[u] - d[v] - son[v], rd2[u] - d2[v] - 2 * d[v] - son[v]);
}

inline void dfs(int n, int f) {
    for (int e = head[f]; ~e; e = edge[e].second) {
        int v = edge[e].first;
        LLONG tmpsonf, tmpdf, tmpd2f;
        std::tie(tmpsonf, tmpdf, tmpd2f) = compute(n, f, v);
        rd2[v] = d2[v] + tmpd2f + 2 * tmpdf + tmpsonf;
        rd[v] = d[v] + tmpdf + tmpsonf;
        dfs(n, v);
    }
}

inline void prepareReverse(int n) {
    rd2[1] = d2[1];
    rd[1] = d[1];
    dfs(n, 1);
}

inline void prepareLca(int n) {
    for (int i = 1; i < K; ++i) {
        for (int j = 1; j <= n; ++j) {
            up[j][i] = up[up[j][i - 1]][i - 1];
        }
    }
}

inline void prepare(int n) {
    memset(head + 1, -1, n * sizeof(head[1]));
    etot = 0;
}

inline int getUpNode(int v, int dis) {
    for (int i = 0; dis; dis >>= 1, ++i) {
        if (dis & 1) {
            v = up[v][i];
        }
    }
    return v;
}

inline int getLca(int u, int v) {
    if (depth[u] > depth[v]) {
        std::swap(u, v);
    }
    v = getUpNode(v, depth[v] - depth[u]);
    if (u == v) {
        return u;
    }
    for (int i = K - 1; i >= 0; --i) {
        if (up[u][i] != up[v][i]) {
            u = up[u][i];
            v = up[v][i];
        }
    }
    return up[u][0];
}

inline int computeDistance(int u, int v) {
    return depth[u] + depth[v] - 2 * depth[getLca(u, v)];
}

int main() {
    int n, q;
    scanf("%d", &n);
    prepare(n);
    for (int i = 2; i <= n; ++i) {
        int p;
        scanf("%d", &p);
        addEdge(p, i);
        parent[i] = p;
    }
    dfs(1);
    prepareReverse(n);
    prepareLca(n);
    for (scanf("%d", &q); q--;) {
        int u, v;
        scanf("%d%d", &u, &v);
        LLONG dis = computeDistance(u, v);
        if (getUpNode(u, dis) == v) {
            if (dis < 1) std::cout << d2[v] << std::endl;
            else {
                int pt = parent[v];
                if (0 == pt) {
                    std::cout << rd2[u] << std::endl;
                } else {
                    ++dis;
                    LLONG tmpdpt, tmpsonpt, tmpd2pt;
                    std::tie(tmpsonpt, tmpdpt, tmpd2pt) = compute(n, pt, v);
                    LLONG rlt = rd2[u] - tmpd2pt - dis * 2 * tmpdpt - dis * dis * tmpsonpt;
                    std::cout << rlt << std::endl;
                }
            }
        } else {
            LLONG rlt = d2[v] + 2 * dis * d[v] + dis * dis * son[v];
            std::cout << rlt << std::endl;
        }
    }
    return 0;
}

Enclosure

  • 题意:由 N(N1e4) 条边组成的多边形,给定每条边的边长,第一条边位置固定,求面积最大时,另外那些点的位置,顺时针摆过来
  • 解法:我们知道,周长固定时,当这个多边形有外接圆的时候面积最大,怎么确定这个圆呢,二分,二分判定的时候就是把除了一条最长边之外的所有边形成的角度加起来和最长边的角度比较往哪边靠,所以我们先以最长的一条边作为直径算此时角度和是不是超过180度(多边形占据半圆内还是半圆多),用来判定二分的时候需要看往哪边靠,想象一下就可以想象出来。
    计算角度用余弦定理。
    最后算位置的时候,定圆心,绕点旋转公式循环一遍 O(n) 可得出,注意如果角度和不超过180度而且第一条边是最长边,圆心在另一边
#include <bits/stdc++.h>

typedef long long LLONG;

const int N = 1e5 + 10;
const double PI = acos(-1);
const double EPS = 1e-8;

int a[N];
double pos[N][2];

inline double computeAcos(double R, double d) {
    return acos(1 - (d * d) / (R * R * 2.0f));
}

inline double computeAngle(int n, double r, int exceptIdx = -1) {
    double rlt = 0;
    for(int i = 0; i < n; ++i) {
        if(i == exceptIdx) { continue; }
        rlt += computeAcos(r, a[i]);
    }
    return rlt;
}

inline void computePosition(int n, double R, bool bigger, int maxIdx) {
    pos[0][0] = 0; pos[0][1] = 0;
    pos[1][0] = 0; pos[1][1] = a[0];
    double circleX = -sqrt(R * R - a[0] * a[0] / 4.0f), circleY = a[0] / 2.0f;
    if(!bigger && 0 == maxIdx) {
        circleX = -circleX;
    }
    for(int i = 1; i < n - 1; ++i) {
        double cosSita = 1 - (a[i] * a[i]) / (R * R * 2.0f);
        double sinSita = sqrt(1 - cosSita * cosSita);
        if(!bigger && i == maxIdx) {
            sinSita = -sinSita;
        }
        double x = pos[i][0] - circleX, y = pos[i][1] - circleY;
        pos[i + 1][0] = x * cosSita - y * sinSita + circleX;
        pos[i + 1][1] = x * sinSita + y * cosSita + circleY;
    }
}

int main() {
    int n;
    scanf("%d", &n);
    double maxl = 0;
    int maxIdx = 0;
    for(int i = 0; i < n; ++i) {
        scanf("%d", a + i);
        if(a[i] > maxl) {
            maxl = a[i];
            maxIdx = i;
        }
    }
    double l = maxl / 2.0f;
    double angle = computeAngle(n, l, maxIdx);
    bool bigger = angle >= PI - EPS;
    double r = 1e8, mid;
    for(; std::fabs(r - l) >= EPS;) {
        mid = (l + r) / 2.0f;
        double angle = computeAngle(n, mid, maxIdx);
        double zero = computeAcos(mid, a[maxIdx]);
        if(bigger) {
            if(zero + angle > 2.0f * PI) {
                l = mid;
            } else {
                r = mid;
            }
        } else {
            if(angle > zero) {
                r = mid;
            } else {
                l = mid;
            }
        }
    }
    computePosition(n, l, bigger, maxIdx);
    for(int i = 0; i < n; ++i) {
        printf("%f\n%f\n\n", -(pos[i][0]), pos[i][1]);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值