2023杭电暑假多校5 题解 1 6 7 12 | JorbanS

1-Typhoon

题意 给定 m m m 个点的坐标 ( x i , y i ) (x_i,y_i) (xi,yi),依次相连,台风在这些线段上移动,给定 n n n 个庇护所的坐标 ( X i , Y i ) (X_i,Y_i) (Xi,Yi),求对每个庇护所而言,台风的影响半径最大为多大时,该庇护所恰好被影响到

Tag 计算几何

题解 先计算 c o s < A B → , A P → > cos<\overrightarrow{AB},\overrightarrow{AP}> cos<AB ,AP > c o s < B A → , B P → > cos<\overrightarrow{BA},\overrightarrow{BP}> cos<BA ,BP > 是否为负,若为负则 P P P 到线段 A B AB AB 的垂足不在线段 A B AB AB 上,则取 m i n ( ∣ P A → ∣ , ∣ P B → ) min(|\overrightarrow{PA}|,|\overrightarrow{PB}) min(PA ,PB ) 即可,否则利用叉乘算 P P P 到线段 A B AB AB 的高度(本质上是算面积),最后取 m i n min min 即可

#include <iostream>
#include <cmath>
#include <vector>
#include <iomanip>

using namespace std;

struct Point { double x, y; };
Point operator+ (Point A, Point B) { return {A.x + B.x, A.y + B.y}; }
Point operator- (Point A, Point B) { return {A.x - B.x, A.y - B.y}; }
double operator* (Point A, Point B) { return A.x * B.x + A.y * B.y; }
double operator^ (Point A, Point B) { return A.x * B.y - A.y * B.x; }

double len(Point P) { return sqrt(pow(P.x, 2) + pow(P.y, 2)); }
double dot(Point P, Point A, Point B) { return (P - A) * (P - B); } // 点乘
double cross(Point A, Point B, Point C) { return (A - B) ^ (A - C); } // 叉乘

vector<Point> typhoon, shelter;

int main() {
    int n, m; cin >> m >> n;
    typhoon.resize(m), shelter.resize(n);
    for (auto &[x, y] : typhoon) cin >> x >> y;
    for (auto &[x, y] : shelter) cin >> x >> y;
    for (auto P : shelter) {
        double res = 2e9;
        for (int i = 1; i < m; i ++) {
            Point A = typhoon[i - 1], B = typhoon[i];
            if (dot(A, P, B) <= 0 || dot(B, P, A) <= 0) {
                res = min(res, min(len(A - P), len(B - P)));
            } else {
                res = min(res, fabs(cross(P, A, B) / len(A - B)));
            }
        }
        cout << fixed << setprecision(4) << res << endl;
    }

    return 0;
}

6-Touhou Red Red Blue

题意 n n n 个红绿蓝球,按顺序操作,给定的字符串包含 R G B RGB RGB 一一对应,有两个袋子,有袋子 1 1 1 2 2 2,每个袋子一次只能装一个球,可以直接扔掉,优先装袋子 1 1 1,每次拿一个球,若两个袋子都满了,有以下三种情况

  1. 若三个球的颜色一致,这三个球扔掉,得一分,颜色自定选择一个球放入袋子
  2. 若三个球的颜色两两不同,这三个球扔掉,颜色自定选择两个的球放入袋子
  3. 扔掉袋子 1 1 1 中的球,将袋子 2 2 2 中的球放到袋子 1 1 1 中,将新拿的球放入袋子 2 2 2

Tag dp

题解 开始初始化初始所有状态情况为 − i n f -inf inf,令初始状态 f [ 0 ] [ 0 ] = 0 f[0][0]=0 f[0][0]=0,考虑如下情况,最后取考虑了前 n n n 个球的情况的所有最终状态的得分,取 m a x max max 即可

状态表示 f [ i ] [ g e t ( a , b ) ] f[i][get(a,b)] f[i][get(a,b)] 表示前 i i i 个球、袋子 1 1 1 a a a 颜色的球、袋子 2 2 2 b b b 颜色的球的最大得分

g e t ( a , b ) = ( a < < 2 ∣ b ) = a × 4 + b get(a,b)=(a<<2|b)=a×4+b get(a,b)=(a<<2∣b)=a×4+b 是对 a a a b b b 状态表示, a a a b b b 均有 0 − 3 0-3 03 四种状态,一共是 16 16 16

状态计算

  • 当直接扔掉该球时,直接继承第 i − 1 i-1 i1 次的状态即可,即 f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j] = f[i - 1][j] f[i][j]=f[i1][j]
  • 当三个球颜色相同时,枚举再拿一个球颜色为 d d d 的情况,并基于第 i − 1 i-1 i1 次加一分,即 f [ i ] [ d ] = m a x ( f [ i ] [ d ] , f [ i − 1 ] [ g e t ( a , b ) ] + 1 ) f[i][d] = max(f[i][d], f[i - 1][get(a, b)] + 1) f[i][d]=max(f[i][d],f[i1][get(a,b)]+1)
  • 当三个球两两颜色不同时,即 f [ i ] [ g e t ( d , e ) ] = m a x ( f [ i ] [ g e t ( d , e ) ] , f [ i − 1 ] [ g e t ( a , b ) ] ) f[i][get(d, e)] = max(f[i][get(d, e)], f[i - 1][get(a, b)]) f[i][get(d,e)]=max(f[i][get(d,e)],f[i1][get(a,b)])
  • 其它情况,更新俩袋子的球,即 f [ i ] [ g e t ( b , c ) ] = m a x ( f [ i ] [ g e t ( b , c ) ] , f [ i − 1 ] [ g e t ( a , b ) ] ) f[i][get(b, c)] = max(f[i][get(b, c)], f[i - 1][get(a, b)]) f[i][get(b,c)]=max(f[i][get(b,c)],f[i1][get(a,b)])
#include <iostream>
#include <cstring>
#define get(a, b) (a << 2 | b)
#define endl '\n'

using namespace std;
const int N = 1e5 + 2, M = 16;
int f[N][M];

int solve() {
    string s; cin >> s;
    int n = s.size();
    for (int i = 1; i <= n; i ++) {
        int c = s[i - 1] == 'R' ? 1 : (s[i - 1] == 'G' ? 2 : 3);
        for (int j = 0; j < M; j ++) f[i][j] = f[i - 1][j]; // 直接扔掉球
        for (int a = 0; a <= 3; a ++)
            for (int b = 0; b <= 3; b ++)
                if (a == b && b == c)
                    for (int d = 1; d <= 3; d ++) f[i][d] = max(f[i][d], f[i - 1][get(a, b)] + 1);
                    // d 放在 2 号袋子是因为下一轮会发现袋子 2 空着, 会执行第三种, (0, d = b) 会变成 (b, c)
        			// 如果 d 放在袋子 1 里面,下一轮会直接把该球扔掉(而此时俩袋子并未同时装满,不符题意)
                else if (a && b && a != b && a != c && b != c)
                    for (int d = 1; d <= 3; d ++)
                        for (int e = 1; e <= 3; e ++)
                            f[i][get(d, e)] = max(f[i][get(d, e)], f[i - 1][get(a, b)]);
                else
                    f[i][get(b, c)] = max(f[i][get(b, c)], f[i - 1][get(a, b)]);
    }
    int res = 0;
    for (int i = 0; i < M; i ++) res = max(res, f[n][i]);
    return res;
}

int main() {
    memset(f[0], 192, sizeof(f[0])); // 初始化为 -inf,表示无法到达这种状态
    f[0][0] = 0; // 起始状态得分为 0
    int T; cin >> T;
    while (T --) cout << solve() << endl;
    return 0;
}

法二:贪心 t = 0 , 1 , 2 t=0,1,2 t=0,1,2 表示从上轮的结果转移过来,这轮要先自选 t t t 个球

  • t = 0 t=0 t=0 时,判断当前球颜色,相应颜色数量自增,判断进入 t = 1 , 2 t=1,2 t=1,2 的条件
  • t = 1 t=1 t=1 时,如果接下来俩球相同,则取和接下来相同的颜色达成“三球颜色相同”进入 t = 1 t = 1 t=1,否则取另外一种颜色的球达成“三球颜色两两不同”进入 t = 2 t=2 t=2 且得分加一
  • t = 2 t=2 t=2 时,一定选择与当前球相同的颜色,使得得分加一,接下来进入 t = 1 t=1 t=1 的情况
#include <iostream>

using namespace std;

int solve() {
    string s; cin >> s;
    int n = s.size(), r = 0, g = 0, b = 0, res = 0, t = 0;
    for (int i = 0; i < n; ) {
        if (t == 1) {
            if (i == n - 1) break; // 只剩两个球,不能得分了
            if (s[i] == s[i + 1]) t = 1, res ++;
            else t = 2;
            i += 2; // 跳过本次和下一次
        } else if (t == 2) {
            t = 1, res ++, i ++; // 跳过本次
        } else {
            if (s[i] == 'R') r ++;
            else if (s[i] == 'G') g ++;
            else b ++;
            if (r && g && b) t = 2;
            else if (r == 3 || g == 3 || b == 3) t = 1, res ++;
            i ++; // 继续
        }
    }
    return res;
}

int main() {
    int T; cin >> T;
    while (T --) cout << solve() << endl;
    return 0;
}

7-Expectation (Easy Version)

题意 k k k 次机会,每次赢的概率为 a b \frac a b ba,第 k k k 次赢得 k m k^m km 分,求总分的期望

Tag 逆元 快速幂 组合数学

题解

期望 = ∑ i = 1 n C n i a i ( b − a ) n − i b n ∑ j = 1 i j m = a b − n ( b − a ) n − 1 ∑ i = 1 n C n i [ a ( b − a ) − 1 ] i − 1 ∑ j = 1 i j m 期望=\sum_{i=1}^{n}C_n^i\frac{a^i(b-a)^{n-i}}{b^n}\sum_{j=1}^{i}j^m =ab^{-n}(b-a)^{n-1}\sum_{i=1}^{n}C_n^{i}[a(b-a)^{-1}]^{i-1}\sum_{j=1}^{i}j^m 期望=i=1nCnibnai(ba)nij=1ijm=abn(ba)n1i=1nCni[a(ba)1]i1j=1ijm

组合数部分 A A = C n i AA=C_n^i AA=Cni

概率部分 B B = a ( b − a ) n − 1 [ a ( b − a ) − 1 ] i − 1 BB=a(b-a)^{n-1}[a(b-a)^{-1}]^{i-1} BB=a(ba)n1[a(ba)1]i1

得分部分 C C = ∑ j = 1 i j m CC=\sum_{j=1}^{i}j^m CC=j=1ijm

答案 r e s = b − n ∑ i = 1 n A A × B B × C C ( m o d p ) res=b^{-n}\sum_{i=1}^{n}AA×BB×CC\pmod p res=bni=1nAA×BB×CC(modp)

#include <iostream>
#define endl '\n'

using namespace std;
typedef long long ll;
const ll mod = 998244353;
ll n, m, a, b;

inline ll qpow(ll a, ll b) {
    ll res = 1;
    while (b) {
        if (b & 1) (res *= a) %= mod;
        (a *= a) %= mod;
        b >>= 1;
    }
    return res;
}

inline ll inv(ll x) {
    return qpow(x, mod - 2);
}

int solve() {
    cin >> n >> m >> a >> b;
    ll res = 0;
    ll k = a * inv(b - a) % mod;
    ll AA = n % mod, BB = qpow(b - a, n - 1) * a % mod, CC = 1;
    for (ll i = 1; i <= n; i ++) {
        (res += AA * BB % mod * CC % mod) %= mod;
        AA = AA * (n - i) % mod * inv(i + 1) % mod;
        (BB *= k) %= mod;
        (CC += qpow(i + 1, m)) %= mod;
    }
    return (res * inv(qpow(b, n))) % mod;
}

signed main() {
    int T; cin >> T;
    while (T --) cout << solve() << endl;
    return 0;
}

12-Counting Stars

题意 无向图中子节点数 i i i 为菊花图的顶点的数量,输出 ⊕ i = 1 m a x ( i ) c n t [ i ] \oplus _{i=1}^{max(i)}cnt[i] i=1max(i)cnt[i]

Tag 快速幂 组合数 逆元

题解 先输入所有节点,得出每个节点的度数,枚举每个节点,计算出 c n t [ i ] , i ∈ [ 2 , m a x ( i ) ] cnt[i],i\in[2,max(i)] cnt[i],i[2,max(i)],最后异或即可

#include <iostream>
#define endl '\n'

using namespace std;
typedef long long ll;
const int N = 1e6 + 2, mod = 1e9 + 7;
int n, m, deg[N], cnt[N];
int fac[N], infac[N];

inline int qpow(int a, int b = mod - 2) {
    int res = 1;
    while (b) {
        if (b & 1) res = (ll)res * a % mod;
        a = (ll)a * a % mod;
        b >>= 1;
    }
    return res;
}

int C(int n, int m) {
    return (ll)fac[n] * infac[m] % mod * infac[n - m] % mod;
}

int solve() {
    cin >> n >> m;
    for (int i = 1; i <= n; i ++) deg[i] = cnt[i] = 0;
    for (int i = 0; i < m; i ++) {
        int x, y; cin >> x >> y;
        deg[x] ++, deg[y] ++;
    }
    for (int i = 1; i <= n; i ++)
        for (int j = 2; j <= deg[i]; j ++)
            (cnt[j] += C(deg[i], j)) %= mod;
    ll res = 0;
    for (int i = 2; i < n; i ++) res ^= cnt[i];
    return res;
}
int main() {
    cin.tie(nullptr) -> sync_with_stdio(false);
    fac[0] = infac[0] = 1;
    for (int i = 1; i < N; i ++) fac[i] = (ll)fac[i - 1] * i % mod;
    infac[N - 1] = qpow(fac[N - 1]);
    for (int i = N - 2; i; i --) infac[i] = (ll)infac[i + 1] * (i + 1) % mod;
    int _; cin >> _;
    while (_ --) cout << solve() << endl;
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JorbanS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值