2019 BUPT Winter Training #3 div2 题解

栏目: Python · 发布时间: 6年前

内容简介:做为一个年近半百的中年人,还能厚着脸皮蹭学弟学妹们的训练赛。真是开心啊。在二维平面上给定一堆整数点,求任意一个三角形,使得三角形内以及其边上,不包含其它的点。我们先从一条边想起。选定任意一个点,从这个点引出的

写在前面

做为一个年近半百的中年人,还能厚着脸皮蹭学弟学妹们的训练赛。真是开心啊。

A - Constellation (CF 618C)

题意

在二维平面上给定一堆整数点,求任意一个三角形,使得三角形内以及其边上,不包含其它的点。

解法

我们先从一条边想起。选定任意一个点,从这个点引出的 最短边 上,一定不包含其它的点。(非常直观)

与这个思路类似,选定这条边之后,从这个边引出的面积最小的三角形,一定也不包含除三角形三点以外的其它点。

求三角形面积可以使用向量的叉积。

代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>

using namespace std;

#define print(x) cout << x << endl
#define input(x) cin >> x

typedef long long llint;

const llint INF = 0x3f3f3f3f3f3f3f3fLL;

struct Point {
    int x, y;
};

int n;
vector<pair<int, int> > points;

int main() {
    input(n);
    points.reserve(n);
    for (int i = 0; i < n; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        points.push_back({x, y});
    }

    int p1 = -1;
    llint d1 = INF;

    for (int i = 1; i < n; i++) {
        llint dx = points[0].first - points[i].first;
        llint dy = points[0].second - points[i].second;

        llint dis = dx * dx + dy * dy;

        if (dis < d1) {
            d1 = dis;
            p1 = i;
        }
    }

    int p2 = -1;
    llint d2 = INF;
    for (int i = 1; i < n; i++) {
        if (i == p1) {
            continue;
        }

        llint dx1 = points[0].first - points[p1].first;
        llint dy1 = points[0].second - points[p1].second;

        llint dx2 = points[0].first - points[i].first;
        llint dy2 = points[0].second - points[i].second;

        llint xmult = abs(dx1 * dy2 - dy1 * dx2);

        if (xmult != 0 && xmult < d2) {
            p2 = i;
            d2 = xmult;
        }
    }

    print(1 << ' ' << p1 + 1 << ' ' << p2 + 1);

    return 0;
}

B - Wet Shark and Flowers (CF 621C)

题意

有一个头尾相接的整数数组,数组中的每个数都在一个给定的范围之内随机选取。如果相邻的两个整数的乘积可以被 p 整除,就可以获得2000分。求给定数组得分的期望。

解法

对于数组中的数来说,得分的期望与其临近的数相关,而当前的数字与相邻的数字在概率上是独立的。所以我们只需要遍历数组,找到相邻数字的得分期望即可。

代码

(n, p) = map(int, raw_input().split())
xs = []

for i in xrange(n):
    l, r = map(int, raw_input().split())
    m = r - l + 1

    l = l if l % p == 0 else (l / p + 1) * p

    if l > r:
        xs.append((1.0, 0))
    else:
        u = (r - l) / p + 1
        pp = 1.0 * u / m
        xs.append((1 - pp, pp))

res = 0
for i in xrange(n):
    p, q = (i - 1 + n) % n, (i + 1) % n
    res += 2000 * xs[i][1] + 2000 * xs[p][1] * xs[i][0]
print res

C - Wet Shark and Odd and Even (CF 621A)

题意

给定一个正整数数组,求这个数组的一个子数组,使得子数组的和最大,且为一个偶数。

解法

如果数组之和为偶数,那它一定是最终答案。如果数组之和为奇数,则找到数组中最小的奇数,减去其值即可。

代码

n = int(raw_input())
ns = map(int, raw_input().split())

s = sum(ns)

if s % 2 == 0:
    print s
else:
    res = 0
    for num in ns:
        if (s - num) % 2 == 0:
            res = max(res, s - num)
    print res

D - Graph and String (CF 624C)

题意

假设我们有一个图,图中的节点被标记为 a,b,c 三个字母中的一个。如果两个节点的标记相同,或者标记相邻(如 <a, b><b, c> ),则两个节点之间一定有一条边。我们称这样的图为“合法图”。

现在给定一张图,问这张图是否合法。如果是,给出任意一个可行解。

解法

本题可以从标记为 b 的节点入手。如果一个节点被标记为 b ,那么它一定和图中任意其它节点都有一条边。然后我们再把其它不相连的节点分别标记为 ac

但是注意,我们做完标记之后的图不一定是合法的。此时我们会再进行一次检查,如果合法则输出解。不合法则输出 No

代码

(n, m) = map(int, raw_input().split())

g = [[0 for i in xrange(n)] for i in xrange(n)]
for i in xrange(m):
    (a, b) = map(int, raw_input().split())
    a -= 1
    b -= 1
    g[a][b] = 1
    g[b][a] = 1

mark = ['' for i in xrange(n)]

def solve1(cur):
    for i in xrange(n):
        if mark[i]:
            continue
        if i == cur or g[cur][i]:
            mark[i] = 'a'
        else:
            mark[i] = 'c'

def check():
    for i in xrange(n):
        for j in xrange(n):
            if i == j:
                continue
            if mark[i] == mark[j] and g[i][j] == 0:
                return False
            if abs(ord(mark[i]) - ord(mark[j])) == 1 and g[i][j] == 0:
                return False
            if abs(ord(mark[i]) - ord(mark[j])) > 1 and g[i][j] == 1:
                return False
    return True


for i in xrange(n):
    if sum(g[i]) == n - 1:
        mark[i] = 'b'

flag = True
for i in xrange(n):
    if mark[i] == '':
        flag = solve1(i)
        break
if check():
    print 'Yes'
    print ''.join(mark)
else:
    print 'No'

E - Array GCD (CF 624D)

题意

给定一个整数数组,规定两种操作:

a
b

问在操作过后,若想使剩下的数组的GCD不为1,所需要付出的最小代价是多少。

解法

我们从结果反推过程。对于一个合法答案来说,一定只包含(修改后的)数组的前缀 A[0...l] 与/或 (修改后的)数组后缀 A[r...n-1] 。所以合法答案中的GCD,一定是前缀或后缀的一个质因子。并且质因子的个数不会超过20(思考:为什么?)

我们枚举这些质因子p。首先求出维护GCD=p的前辍所需要的代价。这里的代价,指的是修改 A[0...l] 加上 删除 A[l + 1...n - 1] 的代价。记录这些值,然后加上一个最小前辍优化。

然后再求出维护GCD=p的后辍需要的代价。这里代价,指的是修改 B[r...n - 1] 的代价, 减去 删除 B[r...n - 1] 的代价。这是因为,在前缀的计算中,我们多减去了后缀的部分。后缀的值与前面的最小前缀进行加和,求出全局最小值即可。

代码

略挫,仅供参考。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>

using namespace std;

#define print(x) cout << x << endl
#define input(x) cin >> x

typedef long long llint;

const int N = 1000007;
const int M = 22;
const llint INF = 0x3f3f3f3f3f3f3f3f;

int n, A, B;
vector<int> ns;
vector<int> primes;

llint dp[N][M];

void get_primes(int num, set<int>& st) {
    if (num <= 1) {
        return;
    }

    if (num % 2 == 0) {
        st.insert(2);
        num /= 2;
    }
    for (llint i = 3; i * i <= num; i += 2) {
        if (num % i == 0) {
            st.insert(i);
        }
        while (num % i == 0) {
            num /= i;
        }
    }

    if (num != 1) {
        st.insert(num);
    }
}

llint solve() {
    memset(dp, INF, sizeof(dp));
    llint ans = INF;

    for (int i = 0; i < primes.size(); i++) {
        llint delta = 1LL * A * n;
        for (int j = 0; j < n; j++) {
            llint mincost = INF;
            for (int k = -1; k <= 1; k++) {
                int u = ns[j] + k;
                llint cost = 1LL * abs(k) * B - A;

                if (u % primes[i] == 0) {
                    mincost = min(mincost, cost);
                }
            }
            if (mincost == INF) {
                break;
            }
            delta += mincost;
            dp[j][i] = delta;
            ans = min(ans, delta);
        }
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < primes.size(); j++) {
            // printf("%lld ", dp[i][j]);
            if (i - 1 >= 0) {
                dp[i][j] = min(dp[i][j], dp[i - 1][j]);
            }
        }
        // puts("");
    }
    // print(ans);

    for (int i = 0; i < primes.size(); i++) {
        llint delta = 0;
        for (int j = n - 1; j >= 0; j--) {
            llint mincost = INF;
            for (int k = -1; k <= 1; k++) {
                int u = ns[j] + k;
                llint cost = 1LL * abs(k) * B - A;

                if (u % primes[i] == 0) {
                    mincost = min(mincost, cost);
                }
            }
            if (mincost == INF) {
                break;
            }
            delta += mincost;
            // print(i << ' ' << j << ' ' << delta);
            if (j - 1 >= 0) {
                ans = min(ans, delta + dp[j - 1][i]);
            }
        }
    }

    return ans;
}

int main() {
    input(n >> A >> B);
    ns.resize(n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &ns[i]);
    }

    set<int> st;
    get_primes(ns[0], st);
    get_primes(ns[n - 1], st);
    get_primes(ns[0] - 1, st);
    get_primes(ns[n - 1] - 1, st);
    get_primes(ns[0] + 1, st);
    get_primes(ns[n - 1] + 1, st);

    primes.reserve(st.size());
    for (auto num: st) {
        primes.push_back(num);
    }

    llint ans = INF;

    ans = min(ans, solve());
    reverse(ns.begin(), ns.end());
    ans = min(ans, solve());

    print(ans);

    return 0;
}

F - Queries about less or equal elements (CF 600B)

题意

求数组B中有多少个数小于等于A[i]。

二分随便搞。解略。

G - Area of Two Circles' Intersection (CF 600D)

题意

给定两个圆,求两个圆相交面积的大小。

解法

做计算几何的最好方法,就是不做计算几何。因为精度太TM烦人了。 —— 介四·窝硕德

题目看上去非常简单,就是求两个弓形的面积之和。这属于高中数学的知识范畴,这里不在赘述。

但是本题的问题在于给定圆的面积太大,我们在进行计算时(尤其是三角函数),容易丢失精度,导致WA。`

本题的很多解法都使用了 long doubleacosl 等高精度浮点数函数。但是这里介绍另外一种解法。

数学好的同学们都知道,三角函数可以通过泰勒公式来计算,而开方可以通过牛顿法来解决。这两个算法都只使用了加减乘除的基本运算,所以几乎不会丢失精度。

Python 语言中,我们有 Decimal 类,可以存储高精度的整数与浮点数。并且在现在的比赛中,越来越多的有支持Python。所以可以备一份模板防身,避免被卡姿势和精度。

代码

import sys
import math
from decimal import *

def sqrt(v):
    a = Decimal(0)
    b = v
    while a != b:
        a = b
        b = (a+v/a)/TWO
    return a

TWO = Decimal(2)
S = 1/sqrt(Decimal(2))
getcontext().prec = 50
PI = Decimal(math.pi)

def taylor_acos(v):
    return PI/2 - taylor_asin(v)

def taylor_asin(v):
    curr = v
    vsq = v**2
    ans = v
    coef = Decimal(1.)
    for i in xrange(3, 101, 2):
        curr *= vsq
        coef = coef * (i-2) / (i-1)
        ans += curr * coef / i
    return ans


def acos(v):
    if v <= S:
        return Decimal(taylor_acos(v))
    return Decimal(taylor_asin(sqrt(1-v**2)))


class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

def point_dis(p1, p2):
    return sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2)

def get_area(l1, l2, l3):
    s = (l1 + l2 + l3) / Decimal(2.0)
    return sqrt(s * (s - l1) * (s - l2) * (s - l3))


(x1, y1, r1) = map(int, raw_input().split())
(x2, y2, r2) = map(int, raw_input().split())

c1 = Point(x1, y1)
c2 = Point(x2, y2)

dis = point_dis(c1, c2)

if dis >= r1 + r2:
    print 0.0
    sys.exit(0)
if dis == 0:
    print PI * (min(r1, r2) ** 2)
    sys.exit(0)

if min(r1, r2) + dis <= max(r1, r2):
    print PI * (min(r1, r2) ** 2)
    sys.exit(0)

r1, r2, dis = map(Decimal, [r1, r2, dis])

a1 = acos((r1 * r1 + dis * dis - r2 * r2) / (2 * r1 * dis)) * 2
a2 = acos((r2 * r2 + dis * dis - r1 * r1) / (2 * r2 * dis)) * 2

s1 = PI * r1 * r1 * (2 * PI - a1) / (2 * PI)
s2 = PI * r2 * r2 * (2 * PI - a2) / (2 * PI)

s3 = 2 * get_area(r1, r2, dis)

print PI * r1 * r1 + PI * r2 * r2 - s1 - s2 - s3

H - Order Book (CF 527B)

题意

很绕。简单来说就是个 排序 问题。

解略。


以上所述就是小编给大家介绍的《2019 BUPT Winter Training #3 div2 题解》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

ANSI Common Lisp

ANSI Common Lisp

Paul Graham / Prentice Hall / 1995-11-12 / USD 116.40

For use as a core text supplement in any course covering common LISP such as Artificial Intelligence or Concepts of Programming Languages. Teaching students new and more powerful ways of thinking abo......一起来看看 《ANSI Common Lisp》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

MD5 加密
MD5 加密

MD5 加密工具