2017 ACM ICPC Asia Regional - Daejeon Programming Constest

A: Broadcast Stations

题目大意

给定一棵树,选一些节点 i ,赋予 P ( i ) 表示节点能覆盖到距离不超过 P ( i ) 的所有点,如果 P ( i ) = 0 ,不能覆盖本身,问 min P ( i )

题解

树形DP。

B: Connect3

题目大意

有一个4*4的竖着放的板,每次可以选择一列从上方放一个黑色或白色的圆盘,圆盘会从下往上磊。已知黑方先选择第x列放了黑色圆盘,问白方最后选择第b列圆盘掉在了第a行恰好白方胜利的情况数有多少。胜利指的是横着、斜着、竖着连续3个圆盘颜色相同即为胜利。
这里写图片描述
(图片从维基找的)

题解

因为范围只有4*4,又有情况的限制,因此合法的情况不会很多,因此我们全部爆搜出来就好了。。

#include <cstdio>
#include <set>
#include <utility>
#define FOR(i,j,k) for(i=j;i<=k;++i)
using namespace std;
set<pair<int, int>> vis;

int a, b, ans = 0;
int board[5][5];

int loc(int x, int y)
{
    return (x - 1) * 4 + (y - 1);
}

bool win(int p)
{
    int i, j;
    FOR(i,1,4) FOR(j,1,4)
    {
        if (i <= 2 && board[i][j] == p && board[i + 1][j] == p && board[i + 2][j] == p)
            return true;
        if (j <= 2 && board[i][j] == p && board[i][j + 1] == p && board[i][j + 2] == p)
            return true;
        if (i <= 2 && j <= 2 && board[i][j] == p && board[i + 1][j + 1] == p && board[i + 2][j + 2] == p)
            return true;
        if (i <= 2 && j >= 3 && board[i][j] == p && board[i + 1][j - 1] == p && board[i + 2][j - 2] == p)
            return true;
    }
    return false;
}

void dfs(int black, int white, int turn, int x, int y)
{
    int i, j;
    if (vis.count(make_pair(black, white)))
        return;
    vis.insert(make_pair(black, white));
    if (win(2) || win(3))
    {
        if (win(3) && turn == 2 && x == a && y == b)
            ++ans;
        return;
    }
    if (board[a][b])
        return;
    FOR(i,1,4) FOR(j,1,4) if (!board[j][i])
    {
        board[j][i] = turn;
        dfs(black | ((turn == 2) << loc(j, i)), white | ((turn == 3) << loc(j, i)), turn ^ 1, j, i);
        board[j][i] = 0;
        break;
    }
}

int main()
{
    int x;
    scanf("%d%d%d", &x, &a, &b);

    board[1][x] = 2;
    dfs(1 << loc(1, x), 0, 3, 1, x);
    printf("%d", ans);

    return 0;
}

C: Game Map

题目大意

给定一个无向图,没有重边,连通。一个合法的序列要求相邻两个元素间有边相连,而且后一个元素比前一个元素的度数要更大,求最长的合法序列的长度。

题解

我们在按度数排序了以后,边就只能向后指(因为向前指的边表示从度数大的点走到度数小的点,是不合法的,可删去),而且端点度数相同的边也要删去(因为要度数严格大于)。那么图就变成了一个DAG,问题变成在DAG上求最长路,DP即可。

#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 100005;
int id[N], dp[N];
vector<int> g1[N], g2[N];
bool cmp(int x, int y) {
    return g1[x].size() < g1[y].size();
}

int main() {
    int n, m, i, x, y;
    scanf("%d%d", &n, &m);
    for (i = 0; i < m; i++) {
        scanf("%d%d", &x, &y);
        g1[x].push_back(y);
        g1[y].push_back(x);    
    }
    for (i = 0; i < n; i++) id[i] = i;
    sort(id, id + n, cmp);
    for (i = 0; i < n; i++)
        for (auto j : g1[i])
            if (g1[i].size() < g1[j].size()) g2[i].push_back(j);

    for (i = 0; i < n; i++)
        for (auto y : G[id[i]])
            dp[y] = max(dp[y], dp[id[i]] + 1);

    int ans = 0;
    for (i = 0; i < n; i++) ans = max(ans, dp[i]);
    printf("%d", ans + 1);
    return 0;
}

D: Happy Number

题目大意

给定函数 f ( x ) 表示 x 每个十进制位的平方和。对 x 不断应用函数 f ,最后可能回到 x 或者到 1 ,问某个数 x 经过多次 f 后是否能到 1

题解

暴力。。

#include <cstdio>
#include <cstring>

int f(int x) {
    int s = 0;
    while (x > 0) {
        int y = x % 10;
        s += y * y;
        x /= 10;    
    }
    return s;
}

int a[1000];

bool test(int x) {
    if (a[x] != -1) return a[x];
    return a[x] = test(f(x));
}

int main() {
    memset(a, -1, sizeof a);
    a[1] = 1;
    a[4] = 0;
    for (int i = 2; i < 1000; i++) test(i);
    int n;
    scanf("%d", &n);
    n = f(n);
    if (a[n]) puts("HAPPY");
    else puts("UNHAPPY");
    return 0;
}

E: How Many to Be Happy?

题目大意

有一个无向图( n 100 , m 500 ),边有边权, H ( e ) 表示对于某条边 e ,至少删去多少条边才能使 e 可能是MST的边。求 H ( e )

题解

对于每条边,边权小于它的边建立容量为1的边,e的两端点分别为源点和汇点,跑一次最大流就能求出必割边有几条。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define rep(i,j,k) for(i=j;i<k;++i)
using namespace std;
const int N = 105, M = 10050, inf = 2147483647;
struct Edge {
    int u, v, w;
    bool operator< (const Edge &b) const {
        return w < b.w;
    }
} e[M];
int h[N], p[M], v[M], w[M], edge = 1, s, t, n;
int level[N], vis[N], q[N];
void add(int a, int b, int c) {
    p[++edge] = h[a]; v[edge] = b; w[edge] = c; h[a] = edge;
    p[++edge] = h[b]; v[edge] = a; w[edge] = 0; h[b] = edge;
}
bool bfs() {
    int i, x, f = 0, r = 0;
    memset(level, -1, sizeof level);
    q[r++] = s; level[s] = 0;
    while (f < r) {
        x = q[f++];
        for (i = h[x]; i; i = p[i])
            if (w[i] && level[v[i]] == -1) {
                level[v[i]] = level[x] + 1;
                q[r++] = v[i];
            }
    }
    return level[t] != -1;
}
int dfs(int x, int low) {
    int i, tmp, res = 0;
    if (x == t) return low;
    for (i = h[x]; i && res < low; i = p[i])
        if (w[i] && level[v[i]] == level[x] + 1) {
            tmp = dfs(v[i], min(low - res, w[i]));
            w[i] -= tmp; w[i ^ 1] += tmp; res += tmp;
        }
    if (!res) level[x] = -1;
    return res;
}
int dinic() {
    int ans = 0, i;
    while (bfs()) {
        ans += dfs(s, inf);
    }
    return ans;
}
int main() {
    int m, i, j, ans = 0;
    scanf("%d%d", &n, &m);
    rep(i,0,m) scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
    sort(e, e + m);

    rep(i,0,m) {
        memset(h, 0, sizeof h); edge = 1;
        s = e[i].u, t = e[i].v;
        rep(j,0,i) if (e[j].w < e[i].w)
            add(e[j].u, e[j].v, 1), add(e[j].v, e[j].u, 1);
        ans += dinic();
    }
    printf("%d\n", ans);
    return 0;
}

F: Philosopher’s Work

题目大意

题解

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
#define x first
#define y second

P tr1(P p) {
    return P(p.y,p.x);
}
P tr2(P p) {
    return P(-p.y, -p.x);
}
P add(P p0, P p1) {
    return P(p0.x + p1.x, p0.y + p1.y);
}

P a[4] = {P(0,0), P(0,1), P(1,1), P(1,0)};

P solve(ll n, ll m) {
    if (n == 2) {
        return a[m];    
    }
    ll t = n * n / 4;
    ll pos = m / t;
    if (pos == 0) {
        return tr1(solve(n / 2, m % t));
    }
    else if (pos == 1) {
        return add(solve(n / 2, m % t), P(0, n / 2));
    }
    else if (pos == 2) {
        return add(solve(n / 2, m % t), P(n / 2, n / 2));
    }
    else {
        return add(tr2(solve(n / 2, m % t)), P(n - 1, n / 2 - 1));
    }

}


int main() {
    ll n, m;
    scanf("%lld%lld", &n, &m);

    --m;
    P ans = add(P(1, 1), solve(n, m));
    cout << ans.x << " " << ans.y << endl;


}

G: Rectilinear Regions

题目大意

有一个阶梯型折线L和U,L和U可能相交后会围成一些区域,求上边界为L,下边界为U的区域个数和总面积。

题解

H: Rock Paper Scissors

题目大意

机器有一个是由石头剪刀布组成的序列,你有一个较短的石头剪刀布序列,你可以选择机器序列的一个等长的子序列玩游戏,对应位如果你赢了算1分,问你最多能赢多少分。

题解

队友想出来的FFT。。

I: Slot Machines

题目大意

你有一个数列,后 n k 位可能有循环节,而且允许最末尾的循环节不完整(尾部被截断),比如123 123 12是合法的,循环节p长度为3,问 k + p 最大时的 k p 为多少

题解

求循环节嘛。。KMP!反转数列后枚举 k 即可。 i n e x t [ i ] 就表示前 i 位的循环节长度。原理参考 https://blog.csdn.net/huanghongxun/article/details/53213871

#include <cstdio>
#include <cstring>
const int N = 1000005;
int next[N], p[N];
int main() {
    int t, i, j, n, ansk, ansp;
    scanf("%d", &n); ansk = ansp = 2 * n;
    for (i = n; i >= 1; --i) scanf("%d", &p[i]);
    for (i = 0, j = 2; j <= n; ++j) {
        while (i && p[j] != p[i + 1]) i = next[i];
        if (p[j] == p[i + 1]) ++i;
        next[j] = i;
    }
    for (i = 1; i <= n; ++i) {
        int p = i - next[i], k = n - i;
        if (p + k < ansp + ansk || p + k == ansp + ansk && p < ansp)
            ansp = p, ansk = k;
    }
    printf("%d %d\n", ansk, ansp);
    return 0;
}

K: Untangling Chain

题目大意

给定一个机器人的运动轨迹,机器人最开始向右,每次只能转90度并至少走1格,轨迹可能有相交处,修改轨迹线段的长度使得轨迹不相交

题解

因为每条线段长度都随便改。。所以我们每次走都走到走过最远处再远一格,记录top、bottom、left、right分别表示走过最上方的y、最下方的y、最左的x和最右的x,向左走走到最左的x-1,其他类似。

#include <cstdio>
#include <algorithm>
using namespace std;

int main()
{
    int left = 0, top = 0, bottom = 0, right = 0;
    int x = 0, y = 0, n, dir = 0, d;
    scanf("%d", &n);
    while (n--)
    {
        scanf("%*d %d", &d);
        switch (dir)
        {
            case 0:
                printf("%d ", (right + 1) - x);
                x = right + 1;
                break;
            case 1:
                printf("%d ", (top + 1) - y);
                y = top + 1;
                break;
            case 2:
                printf("%d ", x - (left - 1));
                x = left - 1;
                break;
            case 3:
                printf("%d ", y - (bottom - 1));
                y = bottom - 1;
                break;
        }
        right = max(right, x); left = min(left, x);
        top = max(top, y); bottom = min(bottom, y);
        dir = (dir + d + 4) % 4;
    }
    return 0;
}

L: Vacation Plans

题目大意

有3个图,从1出发,同时到达每张图的相应终点。走过一条边的时间为1,同时还可以选择在某个点停留一段整数时间。经过一条边和停留在点上都有代价g和h,问总代价最小是多少。

题解

队友有种神奇水法,卡时过了。。留坑。

猜你喜欢

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