SCUACM22暑假集训前劝退赛部分题解

文章目录


传送门

作者:hans774882968以及hans774882968

本文juejin传送门

A

简单几何题,用三角函数知识很容易推(对于考上SCU的同学)。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 1e6 + 5;

int r, a, b, h;

void dbg() {
    puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
    cout << f << " ";
    dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
    Type f = 1;
    char ch;
    xx = 0;
    for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
    xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
    read (x);
    read (r...);
}

int main() {
    read (r, a, b, h);
    if (2 * r <= b) {
        puts ("Drop");
        return 0;
    }
    double H = 1.0 * h * a / (a - b);
    double theta = atan (1.0 * a / 2 / H);
    printf ("Stuck\n%.10lf\n", r / sin (theta) - b / (2 * tan (theta) ) );
    return 0;
}

B

一看就是博弈dp。先打个表

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 5e3 + 5;

int dp[N][N], a[N];
int n, m;

void dbg() {
    puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
    cout << f << " ";
    dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
    Type f = 1;
    char ch;
    xx = 0;
    for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
    xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
    read (x);
    read (r...);
}

bool dfs (int x, int y) {
    if (~dp[x][y]) return dp[x][y];
    dp[x][y] = false;
    if (! (x + y) ) return dp[x][y] = false;
    if (!x || !y) return dp[x][y] = true;
    if (x == y) return dp[x][y] = true;
    if (x < y) return dp[x][y] = dfs (y, x);
    rep (k1, 1, x) {
        for (int k2 = 0; k2 <= y; k2 += k1) {
            dp[x][y] |= !dfs (x - k1, y - k2);
            if (dp[x][y]) break;
        }
    }
    rep (k1, 1, y) {
        for (int k2 = 0; k2 <= x; k2 += k1) {
            dp[x][y] |= !dfs (x - k2, y - k1);
            if (dp[x][y]) break;
        }
    }
    return dp[x][y];
}

void bf (int n) {
    memset (dp, -1, sizeof dp);
    rep (i, 0, n) rep (j, 0, n) dfs (i, j);
    // rep (i, 0, n) rep (j, 0, n) cout << dp[i][j] << " \n"[j == n];
    rep (i, 0, n) rep (j, 0, n) if (!dp[i][j]) dbg (i, j);
    puts ("");
    rep (i, 0, n) rep (j, 0, n) if (!dp[i][j] && i < j) dbg (i, j);
}

int main() {
    bf (300);
    // memset (dp, -1, sizeof dp);
    // int lim = 500;
    // rep (i, 1, lim) {
        // if (a[i]) continue;
        // rep (j, i + 1, lim) {
            // if (!dfs (i, j) ) {
                // a[i] = j;
                // break;
            // }
        // }
        // if (a[i]) a[a[i]] = i;
        // if (i % 100 == 0) cout << "i=" << i << endl; //dbg
    // }
    // rep (i, 1, lim) cout << a[i] << " \n"[i == lim];
    return 0;
}

发现每一行至多一个false,但没有发现其他规律。因此有一种做法是跑上面那段被注释的代码,把每一行的false的点(可能不存在)给找出来。但是要等很久,算了。

正解是:既然为false的点不超过5000,那么就直接用刷表法。根据博弈dp常识,值为true的点推不出任何点,值为false的点可以推出一系列值为true的点。根据调和级数,复杂度O(n^2*logn)说实话这是我第一次看到这种套路

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 5e3 + 5;

bool dp[N][N];
int n, m;

void dbg() {
    puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
    cout << f << " ";
    dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
    Type f = 1;
    char ch;
    xx = 0;
    for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
    xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
    read (x);
    read (r...);
}

void init (int n) {
    rep (i, 0, n) rep (j, 0, n) {
        if (dp[i][j]) continue;
        rep (k1, 1, n - i) {
            for (int k2 = 0; j + k2 <= n; k2 += k1) {
                dp[i + k1][j + k2] = true;
            }
        }
        rep (k1, 1, n - j) {
            for (int k2 = 0; i + k2 <= n; k2 += k1) {
                dp[i + k2][j + k1] = true;
            }
        }
    }
}

int main() {
    init (5000);
    int T;
    read (T);
    while (T--) {
        read (n, m);
        puts (dp[n][m] ? "Alice" : "Bob");
    }
    return 0;
}

E

dp[i]为当前得到的数集的最大值为i,能走的步数的期望。再设ans[i]表示当前得到的数集的最大值为i,能获得的分数的期望。因为题目中,分数只获得1次,所以我们假设当前存在一个数集的最大值,但并没有拿过分数。显然这一构造性假设是合法的。

没有数,就用“有一个0”来表示,于是ans[0]即所求。

值得注意的是,我们也可以用数学形式E(x[i])E(x[i] ^ 2)来表示dp[i]ans[i]

推dp

全期望公式:E(X) = sum(E(X|Y = y[i]) * P(Y = y[i]))

设下一个数为j。当下一个数小于i,能走的步数为1;大于等于i,能走的步数为E(1 + x[j]) = 1 + dp[j]。故由全期望公式:

dp[i] = sum(p[j])(j < i) + sum(p[j] * (1 + dp[j]))(j >= i)

移项得:

dp[i] = (1 + sum(p[j] * dp[j])) / (1 - p[i]) (j > i)

推ans

当下一个数小于i,能获得的分数为1。大于等于i,由上述数学形式得E((1 + x[j]) ^ 2) = E(x[j] ^ 2) + 2 * E(x[j]) + 1 = ans[j] + 2 * dp[j] + 1。故由全期望公式:

ans[i] = sum(p[j])(j < i) + sum(p[j] * (ans[j] + 2 * dp[j] + 1))(j >= i)

移项得:

ans[i] = (1 + 2 * dp[i] * p[i] + sum(p[j] * (ans[j] + 2 * dp[j]))) / (1 - p[i]) (j > i)

总结:关注期望dp的数学记号!

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 5e3 + 5;
const int mod = 998244353;

LL dp[N], ans[N], p[N];
int a[N];
int n;

void dbg() {
    puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
    cout << f << " ";
    dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
    Type f = 1;
    char ch;
    xx = 0;
    for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
    xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
    read (x);
    read (r...);
}

LL q_pow (LL a, LL b) {
    LL r = 1;
    for (; b; b >>= 1) {
        if (b & 1) r = r * a % mod;
        a = a * a % mod;
    }
    return r;
}

int main() {
    read (n);
    rep (i, 1, n) read (a[i]);
    LL isuma = q_pow (accumulate (a + 1, a + n + 1, 0LL), mod - 2);
    rep (i, 1, n) p[i] = a[i] * isuma % mod;
    dwn (i, n, 0) {
        LL s1 = 1, inv1_p = q_pow ( (1 - p[i] + mod) % mod, mod - 2);
        rep (j, i + 1, n)
            s1 = (s1 + p[j] * dp[j] % mod) % mod;
        dp[i] = s1 * inv1_p % mod;
        LL s2 = (1 + 2 * p[i] * dp[i] % mod) % mod;
        rep (j, i + 1, n)
            s2 = (s2 + p[j] * (ans[j] + 2 * dp[j] % mod) % mod) % mod;
        ans[i] = s2 * inv1_p % mod;
    }
    printf ("%lld\n", ans[0]);
    return 0;
}

H

a[i]*a[j] = a[k],以及数据范围1e6都提示我们需要枚举倍数。开个桶计数,然后枚举值ii的倍数j,则贡献为c[i] * c[j] * c[j/i]

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 1e6 + 5;

int n, c[N];

void dbg() {
    puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
    cout << f << " ";
    dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
    Type f = 1;
    char ch;
    xx = 0;
    for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
    xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
    read (x);
    read (r...);
}

int main() {
    read (n);
    int mx = 0;
    rep (_, 1, n) {
        int x;
        read (x);
        c[x]++;
        mx = max (mx, x);
    }
    LL ans = 0;
    rep (i, 1, mx) {
        if (!c[i]) continue;
        for (int j = i; j <= mx; j += i) {
            ans += 1LL * c[i] * c[j] * c[j / i];
        }
    }
    printf ("%lld\n", ans);
    return 0;
}

I

签到。

  • x是s的子集,x为1的部分随便取,x为0但s为1的部分固定为1。
  • x不是s的子集,个数显然为0。

易错点:x == s时0也是答案,但题目问的是正整数,要减掉。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 2e5 + 5;

int x, s;

void dbg() {
    puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
    cout << f << " ";
    dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
    Type f = 1;
    char ch;
    xx = 0;
    for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
    xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
    read (x);
    read (r...);
}

int main() {
    read (x, s);
    if (! ( (x & s) == x && (x | s) == s) ) {
        puts ("0");
        return 0;
    }
    printf ("%lld\n", (1LL << __builtin_popcount (x) ) - (x == s) );
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值