[补题] 第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(上海)

前往独立博客获取更好阅读体验:http://kesisour.work/index.php/archives/90/题源: https://ac.nowcoder.com/acm/contest/9925G Fibonacci签到题,斐波那契数列中偶数的个数为长度的三分之一,贡献值从 n 向下递减/// ~~AK~~ Cu#include <bits/stdc++.h>#define int lltypedef long long ll;using namespace .
摘要由CSDN通过智能技术生成

前往独立博客获取更好阅读体验:http://kesisour.work/index.php/archives/90/

题源: https://ac.nowcoder.com/acm/contest/9925

G Fibonacci

签到题,斐波那契数列中偶数的个数为长度的三分之一,贡献值从 n 向下递减

/// ~~AK~~ Cu
#include <bits/stdc++.h>

#define int ll
typedef long long ll;
using namespace std;

int n;

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    int p = n / 3;
    int sum = (2 * n - p - 1) * p / 2;
    cout << sum << endl;
    return 0;
}

M Gitignore

树上DFS,根据文件目录建树,不能被忽略的节点向上回溯至根节点的所有节点全都不能被忽略,标记完 tag 后从根节点搜索,遇到可以忽略的就返回,否则继续深搜,统计需要忽略的节点个数。

/// ~~AK~~ Cu
#include <bits/stdc++.h>

#define int ll
typedef long long ll;
using namespace std;

const int M = 100005;

int t, n, m;

map<string, int> s;
string st, newst;
int oldcnt = 0, newcnt = 0;
int flag[100005] = {0};
int C = 0, head[100005];

struct edge {
    int to, nxt;
} edge[M];

inline void add_adge(int u, int v) {
    edge[C].to = v;
    edge[C].nxt = head[u];
    head[u] = C;
    C++;
}

int dfs(int x) {
    if (flag[x]) {
        int tmp = 0;
        for (int i = head[x]; i != -1; i = edge[i].nxt) {
            tmp += dfs(edge[i].to);
        }
        return tmp;
    } else return 1;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> t;
    for (int ii = 0; ii < t; ii++) {
        int cnt = 0;
        C = 0;
        memset(head, -1, sizeof(head));
        memset(flag, 0, sizeof(flag));
        flag[0] = 1;
        oldcnt = newcnt = 0;
        s.clear();
        cin >> n >> m;
        for (int i = 0; i < n; i++) {
            cin >> st;
            st = st + '/';
            oldcnt = newcnt = 0;
            for (int j = 0; j < st.length(); j++) {
                if (st[j] == '/') {
                    oldcnt = newcnt;
                    newst = st.substr(0, j);
                    if (!s.count(newst)) {
                        s[newst] = ++cnt;
                        newcnt = s[newst];
                        add_adge(oldcnt, newcnt);
                    }
                    newcnt = s[newst];
                }
            }
        }
        for (int i = 0; i < m; i++) {
            cin >> st;
            st = st + '/';
            for (int j = st.length() - 1; j > 0; j--) {
                if (st[j] == '/') {
                    newst = st.substr(0, j);
                    if (!s.count(newst)) {
                        continue;
                    }
                    newcnt = s[newst];
                    flag[newcnt] = 1;
                }
            }
        }
        int ans = dfs(0);
        cout << ans << endl;
    }
    return 0;
}

B Mine Sweeper II

思维题,考虑数字和就等于相邻(雷格子,非雷格子)二元组的个数,于是把整个地图全反过来这个二元组个数不变,然后 B 与 A 之间以及 B 与 inv(A) ,即把 A 所有格子全部取反的扫雷地图之间总有一个偏差不超过一半的,所以就选其中偏差不超过一半的然后变过去就行。

/// ~~AK~~ Cu
#include <bits/stdc++.h>

#define int ll
typedef long long ll;
using namespace std;

int n, m;

string a[1005], b[1005];

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    for (int i = 0; i < n; i++) {

        cin >> a[i];
    }
    int ans = 0;
    for (int i = 0; i < n; i++)
        cin >> b[i];
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (a[i][j] != b[i][j]) ans++;
        }
    }
    if (ans <= (n * m) / 2) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                cout << a[i][j];
            }
            cout << endl;
        }
    } else {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (a[i][j] == '.')a[i][j] = 'X';
                else a[i][j] = '.';
            }
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                cout << a[i][j];
            }
            cout << endl;
        }
    }
    return 0;
}

D Walker

分类讨论两个点的贡献情况:

  1. 一个点走完所有。
  2. 两个点对着向对方的边界走。
  3. 两个点共同走中间的部分。中间的界限在两边同时贡献完的情况下总时间最短,因此可以二分查找。
    赛场AK代码分类的有点复杂,不过也能正确通过。
/// ~~AK~~ Cu
#include <bits/stdc++.h>

#define int ll
typedef long long ll;
using namespace std;

int t;
double n, p1, p2, v1, v2, tmp;

double fun(double l, double r, double p, double v) {
    double l1 = p - l, l2 = r - p;
    return (min(l1, l2) * 2 + max(l1, l2)) / v;
}

double mid, t1, t2;

double erfen(double l, double r) {
    mid = (l + r) / 2;
    t1 = fun(0, mid, p1, v1);
    t2 = fun(mid, n, p2, v2);
    if (fabs(t1 - t2) <= 1e-6) {
        return t1;
    } else if (t1 < t2) {
        return erfen(mid, r);
    } else {
        return erfen(l, mid);
    }
}

signed main() {
//    ios::sync_with_stdio(false);
//    cin.tie(0);
//    cout.tie(0);
    scanf("%d", &t);
    for (int i = 0; i < t; i++) {
        //cin >> n >> p1 >> v1 >> p2 >> v2;
        scanf("%lf %lf %lf %lf %lf", &n, &p1, &v1, &p2, &v2);
        double ans = 0.0;
        if (p1 - p2 >= 0.00000001) {
            tmp = p1;
            p1 = p2;
            p2 = tmp;
            tmp = v1;
            v1 = v2;
            v2 = tmp;
        }
        if (v1 - v2 >= 0.00000001) {
            tmp = p1;
            p1 = n - p2;
            p2 = n - tmp;
            tmp = v1;
            v1 = v2;
            v2 = tmp;
        }
        if (p1 == p2) {
            ans = max(p1 / v1, (n - p2) / v2);
        }
        else if ((n - p2) / v2 - fun(0, p2, p1, v1) >= 0.00000001) {
            ans = (n - p2) / v2;
        }
        else if (p1 / v1 - fun(0, n, p2, v2) >= 0.00000001) {
            ans = fun(0, n, p2, v2);
        } else if (p1 / v1 - fun(p1, n, p2, v2) >= 0.00000001) {
            ans = p1 / v1;
        } else {
            /// 二分 x
            ans = erfen(p1, p2);
        }
        double ans2 = max((n - p1) / v1, p2 / v2);
        ans = min(ans, ans2);
        printf("%.10lf\n", ans);
    }
    return 0;
}

C Sum of Log

根据性质可以得出 log2(i + j) == log2(max(i, j)),即最大为1的二进制位不会改变,考虑按二进制位进行数位DP。
dp[i][j][k][l]表示当前第i位,j&k 都是 0/1 变量,表示当前状态是否抵住了X&Y的上界,l表示当前状态的最高有效位。
可能实现方法略微复杂,甚至在交题的时候有一定概率会T…,抽空补一下递推的数位DP版本。

#include <bits/stdc++.h>

#define numm ch - 48
#define pd putchar(' ')
#define pn putchar('\n')
#define int ll
typedef long long ll;
using namespace std;

template<typename T>
void read(T &res) {
    bool flag = false;
    char ch;
    while (!isdigit(ch = getchar())) (ch == '-') && (flag = true);
    for (res = numm; isdigit(ch = getchar()); res = (res << 1) + (res << 3) + numm);
    flag && (res = -res);
}

template<typename T>
void write(T x) {
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}

///

const int mod = 1e9 + 7;

int t, x, y, tmp, lx, ly;
int digx[35], digy[35];
int dp[35][2][2][35];

int dfs(int dig, int limx, int limy, int maxdig) {
    if (!dig) return maxdig;
    if (dp[dig][limx][limy][maxdig]) return dp[dig][limx][limy][maxdig];
    int ans = 0;
    if ((limx and digx[dig]) or !limx) ans += dfs(dig - 1, limx, limy and (!digy[dig]), (maxdig ? maxdig : dig));
    if ((limy and digy[dig]) or !limy) ans += dfs(dig - 1, limx and (!digx[dig]), limy, (maxdig ? maxdig : dig));
    ans += dfs(dig - 1, limx and (!digx[dig]), limy and (!digy[dig]), maxdig);
    dp[dig][limx][limy][maxdig] = ans % mod;
    return dp[dig][limx][limy][maxdig];
}

signed main() {
    read(t);
    while (t--) {
        memset(dp, 0, sizeof(dp));
        memset(digx, 0, sizeof(digx));
        memset(digy, 0, sizeof(digy));
        read(x), read(y);
        tmp = x, lx = 0, ly = 0;
        while (tmp) {
            digx[++lx] = tmp % 2;
            tmp = tmp >> 1;
        }
        tmp = y;
        while (tmp) {
            digy[++ly] = tmp % 2;
            tmp = tmp >> 1;
        }
        write(dfs(max(lx, ly), 1, 1, 0)), pn;
    }
    return 0;
}

I Sky Garden

思维题,挺简单的,注意 m 为 1 的时候圆心是没有端点的。

#include <bits/stdc++.h>

#define numm ch - 48
#define pd putchar(' ')
#define pn putchar('\n')
#define int ll
typedef long long ll;
using namespace std;

template<typename T>
void read(T &res) {
    bool flag = false;
    char ch;
    while (!isdigit(ch = getchar())) (ch == '-') && (flag = true);
    for (res = numm; isdigit(ch = getchar()); res = (res << 1) + (res << 3) + numm);
    flag && (res = -res);
}

template<typename T>
void write(T x) {
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}



///

const double pi = 3.1415926535;

int n, m;
double ans;

signed main() {
    read(n), read(m);
    if (m != 1) ans = n * m * (n + 1);
    for (int i = 1; i <= n; i++) {
        double tmp = 0;
        for (int j = 1; j <= m; j++) {
            tmp += min((double)2 * i, pi * i * j / m);
        }
        tmp *= 2.0 * m;
        tmp -= 2.0 * m * i;
        ans += tmp;
    }
    for (int i = 1; i < n; i++) {
        double tmp = 0;
        for (int j = 1; j <= m; j++) {
            tmp += min((double)2 * i, pi * i * j / m);
        }
        tmp *= 2.0;
        tmp -= 2.0 * i;
        tmp *= 2.0 * m;
        tmp *= n - i;
        ans += tmp;
        ans += (double) m * (n - i + 1) * (n - i) * 2 * m;
    }
    printf("%.8f", ans);
    return 0;
}

L Traveling in the Grid World

思维题

若 gcd(m - a, n - b) == 1,则 (m, n) 能否从点 (a, b) 直接到达。

第一个结论:不能直接到达的点最多有一个转折点就可以到达。
证明:gcd(1, n - 1) == 1 and gcd(m - 1, 1) == 1一定成立。

第二个结论:若某转移点使得总距离最小,则途中两条路一定不会经过点。
证明:若从 A 到 B 经过转移点 C,此时有点 D 位于 AC 路径中,那么 AD + BD < AC + BC

第三个结论:最优解一定只需要一个转移点。
证明:若从 A 到 B 经过两个转移点 C 和 D,那么总路径为 AC + CD + BD,此时BC > BD + CD。若 BC 可以直接到达,则不需要点 D 答案更优,若 BC 不能直接到达,则参见第二结论, BC 中一定存在点 E 使得 AE + BE < AC + BC,答案更优。

综上,若不能直接到达,暴力直线附近的点作为转移点即可。

#include <bits/stdc++.h>

#define numm ch - 48
#define pd putchar(' ')
#define pn putchar('\n')
#define int ll
typedef long long ll;
using namespace std;

template<typename T>
void read(T &res) {
    bool flag = false;
    char ch;
    while (!isdigit(ch = getchar())) (ch == '-') && (flag = true);
    for (res = numm; isdigit(ch = getchar()); res = (res << 1) + (res << 3) + numm);
    flag && (res = -res);
}

template<typename T>
void write(T x) {
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}



///

int t, n, m;
int f, c;

signed main() {
    read(t);
    while (t--) {
        read(n), read(m);
        if (__gcd(m, n) == 1) {
            printf("%.9lf\n", sqrt(m * m + n * n));
            continue;
        }
        double k = (double) m / n;
        double ans = 200000005.0;
        f = 1, c = 0;
        while (f) {
            for (int i = 1; i <= n; i++) {
                int y = k * i - c;
                if (fabs((double)y / i - k) < 1e-10) continue;
                if (__gcd(i, y) == 1 and __gcd(n - i, m - y) == 1) {
                    f = 0;
                    ans = min(ans, sqrt(y * y + i * i) + sqrt((n - i) * (n - i) + (m - y) * (m - y)));
                }
            }
            c++;
        }
        printf("%.9lf\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值