2017 ACM ICPC East Central North America Regional Contest

手速场。。就不放队友的代码了。

A: Abstract Art

题目大意

给定多个多边形,求面积并。

题解

模板题。

B: Craters

题目大意

给定多个圆,求这些圆的凸包周长。

题解

一个圆最多引出两条切线,计算切线注意一些情况就好了。
不过我的做法是暴力将圆拆成3000个点,然后暴力求背包。

#include <bits/stdc++.h>
using namespace std;
const int sigma = 3000;
const int N = 1000050;
typedef double db;
const db eps = 1e-8;
const db PI = acos(-1.0);
inline int dcmp(db x)
{
    return x < -eps ? -1 : x > eps;
}

struct P
{
    db x, y;
    int id;

    P(db x = 0, db y = 0) : x(x), y(y) {}

    P operator+(P a) const { return P(x+a.x,y+a.y); }
    P operator-(P a) const { return P(x-a.x,y-a.y); }
    P operator*(db p) const { return P(p * x, p * y); }
    P operator/(db p) const { return P(x / p, y / p); }
    bool operator<(P a) const { return x < a.x || (dcmp(x - a.x) == 0 && y < a.y); }
    bool operator==(P a) const { return dcmp(x - a.x) == 0 && dcmp(y - a.y) == 0; }
};

typedef P V;

inline db dot(V a, V b)
{
    return a.x * b.x + a.y * b.y;
}

inline db len(V a)
{
    return sqrt(dot(a, a));
}

inline db dis(P a, P b)
{
    return len(b - a);
}

inline db ang(V a, V b)
{
    return acos(dot(a, b) / len(a) / len(b));
}

inline db cross(V a, V b)
{
    return a.x * b.y - a.y * b.x;
}

inline db area(P a, P b, P c)
{
    return cross(b - a, c - a);
}

V rot(V a, db p)
{
    return V(a.x * cos(p) - a.y * sin(p), a.x * sin(p) + a.y * cos(p));
}

V normal(V a)
{
    db L = len(a);
    return V(-a.y / L, a.x / L);
}

P inter(P p, V v, P q, V w)
{
    V u = p - q;
    db t = cross(w, u) / cross(v, w);
    return p + v * t;
}

db dis(P p, P a, P b)
{
    V v1 = b - a, v2 = p - a;
    return fabs(cross(v1, v2)) / len(v1);
}

db dis2(P p, P a, P b)
{
    if (a == b) return len(p - a);
    V v1 = b - a, v2 = p - a, v3 = p - b;
    if (dcmp(dot(v1, v2)) < 0) return len(v2);
    else if (dcmp(dot(v1, v3)) > 0) return len(v3);
    else return fabs(cross(v1, v2)) / len(v1);
}

P proj(P p, P a, P b)
{
    V v = b - a;
    return a + v * (dot(v, p - a) / dot(v, v));
}

bool isInter(P a1, P a2, P b1, P b2)
{
    db c1 = cross(a2 - a1, b1 - a1), c2 = cross(a2 - a1, b2 - a1),
        c3 = cross(b2 - b1, a1 - b1), c4 = cross(b2 - b1, a2 - b1);
    return dcmp(c1) * dcmp(c2) < 0 && dcmp(c3) * dcmp(c4) < 0;
}

bool onSeg(P p, P a1, P a2)
{
    return dcmp(cross(a1 - p, a2 - p)) == 0 && dcmp(dot(a1 - p, a2 - p)) < 0;
}

db area(P p[], int n)
{
    db s = 0;
    p[n] = p[0];
    for (int i = 1; i < n; ++i)
        s += cross(p[i] - p[0], p[i + 1] - p[0]);
    return s / 2;
}

int graham(P p[], int n, P ch[])
{
    sort(p, p + n);
    int m = 0;
    for (int i = 0; i < n; ++i)
    {
        while (m > 1 && cross(ch[m - 1] - ch[m - 2], p[i] - ch[m - 2]) <= 0) --m;
        ch[m++] = p[i];
    }
    int k = m;
    for (int i = n - 2; i >= 0; i--)
    {
        while (m > k && cross(ch[m - 1] - ch[m - 2], p[i] - ch[m - 2]) <= 0) --m;
        ch[m++] = p[i];
    }
    if (n > 1) m--;
    return m;
}

struct C
{
    P c;
    db r, delta;
} c[205];

P p[N], ch[N];

int main()
{
    int n, m, i, j, l = 0;
    db ans = 0;
    scanf("%d", &n);
    for (i = 0; i < n; ++i)
    {
        scanf("%lf%lf%lf", &c[i].c.x, &c[i].c.y, &c[i].r);
        c[i].r += 10;
        c[i].delta = 2.0 * PI * c[i].r / sigma;
        for (j = 0; j < sigma; ++j)
        {
            db theta = 2.0 * PI * j / sigma;
            p[l] = c[i].c + P(c[i].r * cos(theta), c[i].r * sin(theta));
            p[l].id = i;
            l++;
        }
    }
    m = graham(p, l, ch);
    for (i = 0; i < m; ++i)
    {
        P x = ch[i], y = ch[(i + 1) % m];
        if (x.id == y.id) ans += c[x.id].delta;
        else ans += dis(x, y);
    }
    printf("%.10lf", ans);

    return 0;
}

C: DRM Messages

模拟。

#include <bits/stdc++.h>
using namespace std;

int main()
{
    string str;
    cin >> str;
    size_t n = str.length(), x;
    string l = str.substr(0, n / 2);
    string r = str.substr(n / 2, n / 2);
    x = 0; for (auto ch : l) x += ch - 'A';
    for (auto &ch : l) ch = (ch - 'A' + x) % 26 + 'A';
    x = 0; for (auto ch : r) x += ch - 'A';
    for (auto &ch : r) ch = (ch - 'A' + x) % 26 + 'A';
    for (size_t i = 0; i < n / 2; ++i)
        l[i] = (l[i] - 'A' + r[i] - 'A') % 26 + 'A';
    cout << l;

    return 0;
}

D: Game of Throwns

题目大意

n个人围成一圈,第0号人一开始拿着手帕,每次从第 x 人丢给第 x + p 人,或者回退 m 次操作,问最后手帕在那个人手上。

题解

模拟。

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

int main()
{
    stringstream ss;
    stack<ll> sk;
    ll n, k, p;
    string str;
    cin >> n >> k;
    for (int i = 0; i < k; ++i)
    {
        cin >> str;
        if (str == "undo")
        {
            cin >> p;
            for (int j = 0; j < p; ++j)
                sk.pop();
        }
        else
        {
            ss.clear();
            ss << str;
            ss >> p;
            sk.push(p);
        }
    }

    ll ans = 0;
    while (!sk.empty())
    {
        ans += sk.top();
        sk.pop();
    }

    cout << (ans % n + n) % n << endl;

    return 0;
}

E: Is-A? Has-A? Who Knowz-A?

题目大意

我们定义A is-a B表示AB的子类,A has-a B表示A有一个成员B,也就是说如果存在B is-a C; A has-a B,那么A has-a C;如果A is-a C; C has-a B,那么A has-a B;如果B is-a C; A is-a C,那么A is-a C;如果B has-a C; A has-a C,那么A has-a C

题解

模拟。

#include <bits/stdc++.h>
using namespace std;
const int N = 10005;
vector<int> has_fa[N], is_super[N], is_sub[N];
bool vis_has[N], vis_is[N];
bool has[N][N], is[N][N];

void dfs_has(int x, int y)
{
    vis_has[x] = true;
    for (auto fa : has_fa[x]) // 已知x has y,若fa has x,则fa has y,has具有传递性
        if (!has[fa][y])
        {
            has[fa][y] = true;
            dfs_has(fa, y);
        }
    for (auto sub : is_sub[x])
        if (has[x][y])
        {
            if (!has[sub][y])
            { // 如果x has y,若sub is x,那么sub has y,若父类有一个成员,那么子类也会有这个成员
                has[sub][y] = true;
                dfs_has(sub, y);
            }
        }
        else
        { // 如果x !has y(x is-a y的情况),若? has sub,则? has x,那么? has y
            if (!vis_has[sub]) 
                dfs_has(sub, y);
        }
}

void dfs_is(int x, int sub)
{
    vis_is[x] = is[sub][x] = true;
    for (auto fa : is_super[x]) // is 具有传递性
        if (!vis_is[fa])
            dfs_is(fa, sub);
}

size_t id = 0;
size_t get_id(string const& str)
{
    static map<string, size_t> mp;
    return !mp[str] ? mp[str] = ++id : mp[str];
}

int main()
{
    string c1, r, c2;
    int n, m;
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for (int i = 0; i < n; ++i)
    {
        cin >> c1 >> r >> c2;
        if (r == "is-a")
        {
            is_super[get_id(c1)].push_back(get_id(c2));
            is_sub[get_id(c2)].push_back(get_id(c1));
        }
        else
        {
            has_fa[get_id(c2)].push_back(get_id(c1));
        }
    }

    for (int i = 1; i <= id; ++i)
    {
        memset(vis_has, 0, sizeof vis_has);
        memset(vis_is, 0, sizeof vis_is);
        dfs_has(i, i);
        dfs_is(i, i);
    }

    for (int i = 1; i <= m; ++i)
    {
        cin >> c1 >> r >> c2;
        if (r == "is-a")
        {
            printf("Query %d: %s\n", i, is[get_id(c1)][get_id(c2)] ? "true" : "false");
        }
        else
        {
            printf("Query %d: %s\n", i, has[get_id(c1)][get_id(c2)] ? "true" : "false");
        }
    }
    return 0;
}

F: Keeping on Track

题目大意

给定一棵树,删掉某个唯一的节点,满足条件是删掉这个点后不连通的点对数最多。求删掉这个点后不连通的点对数和连了一条边后最少不连通的点对数。

题解

显然我们先预处理出所有节点的子树的大小(如果以该节点为根的话),那么不连通的点对数就是两两点数积之和了。选择点数积最大的两个子树连起来就能得到最少的点对数。

G: A Question of Ingestion

题目大意

一个人吃饭,每小时上一道菜,卡路里是 c i ,这个人最开始吃的时候可以吃 m 卡路里的食物,第二个小时如果连续吃则会吃 2 3 m 卡路里的食物,如果第三个小时继续吃则会吃最多 4 9 m 卡路里的食物,如果第三个小时不吃,那么第四个小时吃的话最多能吃 2 3 m 的食物。比如 m = 900 ,那么可能的序列有:900 600 400900 600 0 600900 600 0 0 900,也就是说,停顿一小时可以恢复到上一次吃卡路里最多的量。休息两小时直接恢复到初始值问最多能吃多少卡路里。

题解

一个简单的dp,令dp[i][j]表示对于第i个小时,只能吃 v a l j = ( 2 3 ) j m 的食物获得的最多的卡路里有多少。那么显然有:

dp[i][j] = max {
    dp[i + 1][j], // 当前时刻不吃,j不变
    dp[i + 1][j + 1] + min(c[i], val[j]), // 当前时刻吃,下一个时刻也吃
    dp[i + 2][j] + min(a[i], val[j]), // 当前时刻吃,下一个时刻不吃,下下时刻吃
    dp[i + 3][0] + min(a[i], val[j]) // 当前时刻吃,休息2小时,恢复到初始值
}

H: Sheba’s Amoebas

题目大意

一个格子与周围的8个格子均相邻,找出最小环的个数。

题解

爆搜。。

I: Twenty Four, Again

题目大意

二十四点,但不允许最开始的正负号,比如-3+5*5+2是不可以的,因为3前面不能有符号;只能进行整除,也就是说2/3*4*9是不可以的。对于给定的四个数字,交换相邻的两个元素的代价是2,比如3 5 5 25 5 3 2的代价是4。添加一次括号的代价是1,比如(3+5)*(5-2)的代价是2。计算得到二十四点的最小代价。

题解

暴力题。暴力求出所有的排列交换元素的代价,这个bfs就可以得到, O ( 4 ! ) 。然后再暴力枚举所有的运算符 O ( 4 3 ) 。然后暴力搜索加括号的情况,最后计算结果即可(仍然要注意优先级)。

J: Workout for a Dumbbell

题目大意

你有十台机器,要完成一件事,需要按顺序从第一台用到第十台,用3轮,总共30次机器。每台机器都有一个固定的用户不断地使用那台机器。使用一个机器指的是先花usage的时间占用这台机器生产,然后花费recovery的时间手工完成剩下的工作,recovery的时间不占用机器时间,也就是说,如果机器的固定用户处于recovery的状态时,你就可以使用这台机器,由于机器同时只能处理一个任务,所以一个人占用了机器后,另一个人要等到原来的人用完了才能继续用,如果固定用户和你都想用同一台机器,那么固定用户优先。计算你最后需要花多少时间(不需要计算最后一次的recovery的时间)。

题解

模拟题。。

#include <bits/stdc++.h>
using namespace std;
const int N = 16;
int ju[N], jr[N], u[N], r[N], t[N];

int main()
{
    int i, j, now = 0;
    for (i = 1; i <= 10; ++i)
        scanf("%d%d", &ju[i], &jr[i]);
    for (i = 1; i <= 10; ++i)
        scanf("%d%d%d", &u[i], &r[i], &t[i]);

    for (i = 1; i <= 3; ++i)
        for (j = 1; j <= 10; ++j)
        {
            if (t[j] < now)
                t[j] += (now - t[j]) / (u[j] + r[j]) * (u[j] + r[j]);
            if (t[j] <= now)
            {
                t[j] += u[j];
                now = max(now, t[j]) + ju[j];
                t[j] = max(t[j] + r[j], now);
            }
            else
            {
                now += ju[j];
                t[j] = max(t[j], now);
            }
            now += jr[j];
        }

    printf("%d", now - jr[10]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/huanghongxun/article/details/80034936