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

Posted hans774882968

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 2021SCUACM集训队冬季选拔2大部分题解

2021SCUACM集训队冬季选拔2大部分题解

UESTC1608 暑假集训

UESTC2021暑假前集训(主席树)

[NEFU ACM大一暑假集训 解题报告]前缀和与差分

正睿2018暑假集训 比赛题选做