AtCoder Beginner Contest 315 题解

AtCoder Beginner Contest 315 题解

Contest

D , F , G D,F,G D,F,G 题解


D - Magical Cookies

题意

给定一个 h h h × w w w 的小写字母字符矩阵,现在一次执行如下操作:

  1. 对于每一行,如果这一行剩下两个以上的字符,并且全部相同,删除这一行全部的字符。同时每一列,如果这一列剩下两个以上的字符,并且全部相同,删除这一列全部的字符。
  2. 如果第 1 1 1 步有删除字符,跳转到第 1 1 1 步,否则结束。

求出最后剩下几个字符?

解法

一轮一轮进行删除操作,最多只进行 ( h + w ) (h + w) (h+w) 轮操作;

对于每轮操作,需要检查所有行与列,即 ( h + w ) (h + w) (h+w)

每一次检查,只需要枚举 26 26 26 种小写字母即可.

#include <bits/stdc++.h>
#define For(i, a, b) for (ll i = a; i <= b; i++)
#define _For(i, a, b) for (ll i = a; i >= b; i--)
typedef long long ll;
using namespace std;
const int maxn = 2e3 + 5;
int n, m;
int cntx[30][maxn], cnty[30][maxn], resx[maxn], resy[maxn]; 
void solve() {
    cin >> n >> m;
    int ansx = n, ansy = m;
    For (i, 1, n) {
        For (j, 1, m) {
            char c;
            cin >> c;
            cntx[c - 'a'][i]++;
            cnty[c - 'a'][j]++;
        }
    }

    For (i, 1, n) resx[i] = m;
    For (i, 1, m) resy[i] = n;
    For (loop, 1, n + m) {
        vector <pair<int, int>> delx;
        vector <pair<int, int>> dely;
        For (i, 1, n) {
            if (!resx[i]) continue;
            For (l, 0, 25) {
                if (cntx[l][i] == resx[i] && resx[i] > 1) {
                    ansx--;
                    resx[i] = 0;
                    cntx[l][i] = 0;
                    delx.push_back({i, l});
                }
            }
        }

        For (i, 1, m) {
            if (!resy[i]) continue;
            For (l, 0, 25) {
                if (cnty[l][i] == resy[i] && resy[i] > 1) {
                    ansy--;
                    resy[i] = 0;
                    cnty[l][i] = 0;
                    dely.push_back({i, l});
                }
            }
        }

        for (auto i: delx) {
            int x = i.first, c = i.second;
            For (i, 1, m) {
                resy[i]--, cnty[c][i]--; 
            }
        }

        for (auto i: dely) {
            int y = i.first, c = i.second;
            For (i, 1, n) {
                resx[i]--, cntx[c][i]--; 
            }
        }        
    }

    cout << ansx * ansy;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);

    int t;
    t = 1;
    while (t--) {
        solve();
    }

    return 0;
}

F - Shortcuts

题意

在一个二维平面上有 n n n 个点,你需要按顺序从点 1 1 1 走到点 n n n

你可选择跳过 c c c 个点不走(不能跳过起点和终点),跳过的惩罚是 $2^{c-1} $ (特别的, c = 0 c = 0 c=0 时,惩罚为 0 0 0 )。

求出走的距离加上惩罚的最小值。

解法

在跳过 30 30 30 个点后,惩罚已经有 1 0 9 10^9 109 ,已经超过了距离和,所以最多可跳点数不超过 k = 30 k = 30 k=30 个点;

动态规划,设 d p [ i ] [ j ] dp[i][j] dp[i][j] 为到达 i i i ,已经跳过了 j j j 个节点的最小距离(不包括惩罚);

递推式为 d p [ i + j + l + 1 ] [ j + l ] = m i n ( d p [ i + j + l + 1 ] [ j + l ] , d p [ i + l ] [ l ] + s q r t ( d i s ) ) dp[i + j + l + 1][j + l] = min(dp[i + j + l + 1][j + l], dp[i + l][l] + sqrt(dis)) dp[i+j+l+1][j+l]=min(dp[i+j+l+1][j+l],dp[i+l][l]+sqrt(dis)) .

#include <bits/stdc++.h>
#define For(i, a, b) for (ll i = a; i <= b; i++)
#define _For(i, a, b) for (ll i = a; i >= b; i--)
typedef long long ll;
using namespace std;
const int maxn = 1e4 + 5;
int n;

void solve() {
    cin >> n;
    vector <pair<int, int>> a(n + 1); 
    For (i, 1, n) cin >> a[i].first >> a[i].second;
    vector <vector <double> > dp(n + 1,vector <double> (35, 1e10));
    dp[1][0] = 0;
    For (i, 1, n) {
        For (j, 0, 30) {
            For (l, 0, 30) {
                if (j + l > 30 || i + j + l + 1 > n) break;
                double dis = (a[i + l + j + 1].second - a[i + l].second) * (a[i + l + j + 1].second - a[i + l].second) + (a[i + l + j + 1].first - a[i + l].first) * (a[i + l + j + 1].first - a[i + l].first);
                dp[i + j + l + 1][j + l] = min(dp[i + j + l + 1][j + l], dp[i + l][l] + sqrt(dis));
            }
        }
    }
    double ans = 1e10;
    For (i, 0, 30) {
        ans = min(ans, dp[n][i] + (i == 0 ? 0 : pow(2, i - 1)));
    }
    cout << fixed << setprecision(20) << ans;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);

    int t;
    t = 1;
    while (t--) {
        solve();
    }

    return 0;
}

G - Ai + Bj + Ck = X (1 <= i, j, k <= N)

题意

求出 A i + B j + C k = X Ai + Bj + Ck = X Ai+Bj+Ck=X 的整数解 ( i , j , k ) (i, j, k) (i,j,k) 的个数(要求 1 ⩽ i , j , k ⩽ N 1 \leqslant i,j,k \leqslant N 1i,j,kN ).

解法

模板题

前置知识: 拓展欧几里得算法

考虑子问题:求出 a x + b y = c ax + by = c ax+by=c 的整数解 ( x , y ) (x, y) (x,y) 的个数.

解答:当 c c c ∤ \nmid g c d ( a , b ) gcd(a, b) gcd(a,b) 时,方程无解。否则可通过拓展欧几里得求出一组解 x 0 , y 0 x_0, y_0 x0,y0 。其他所有解为 x = x 0 + k b g c d ( a , b ) , y = y 0 − k a g c d ( a , b ) ( k ∈ Z ) x = x_0 + \frac{kb}{gcd(a, b)}, y = y_0 - \frac{ka}{gcd(a, b)} (k\in \mathbb{Z}) x=x0+gcd(a,b)kb,y=y0gcd(a,b)ka(kZ) 。可以通过解不等式求出 k k k 的范围。

对于本题,枚举 x x x 1 ∼ N 1 \sim N 1N 中的每一个值,然后利用前置知识求出 j , k j, k j,k 的可行解数量,最后加在一起。复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

EXGCD总结

#include <bits/stdc++.h>
#define For(i, a, b) for (ll i = a; i <= b; i++)
#define _For(i, a, b) for (ll i = a; i >= b; i--)
typedef long long ll;
using namespace std;

/**
 * @brief exgcd 求方程 ax + by = gcd(a, b) 的一组整数解
 * @return 返回 gcd(a, b)
 */

ll exgcd(ll a, ll b, ll &x, ll &y) {
    if (!b) {
        x = 1, y = 0;
        return a;
    }
    ll d = exgcd(b, a % b, y, x);
    y -= (a / b) * x;
    return d;
}

void solve() {
    ll n, a, b, c, X;
    cin >> n >> a >> b >> c >> X;
    ll ans = 0;
    For (i, 1, n) {
        ll k = X - a * i;
        ll x, y;
        ll d = exgcd(b, c, x, y);
        ll tx = c / d, ty = b / d;
        if (k % d) continue;
        x = (k / d % tx * x % tx + tx) % tx; 
        y = (k - b * x) / c;

        ll l1, l2, r1, r2;
        l1 = ceil(1.0 * (1ll - x) / tx);
        l2 = ceil(1.0 * (y - n) / ty);
        r1 = floor(1.0 * (n - x) / tx);
        r2 = floor(1.0 * (y - 1ll) / ty);
        l1 = max(l1, l2);
        r1 = min(r1, r2);
        if (r1 >= l1)
            ans += r1 - l1 + 1;
    }
    cout << ans;
/*    
    ll a, b, k, x, y;
    // 求 ax + by = k 的解
    cin >> a >> b >> k;
    ll d = exgcd(a, b, x, y);
    ll tx = b / d, ty = a / d;
    if (k % d) { puts("No Solution"); return; }
    else {
        x = x * k / d;
        // x = (k / d % tx * x % tx + tx) % tx;
        y = (k - a * x) / b;
        
        根据拓展欧几里得求出的一组解,x0, y0
        其他解为 x = x0 + k * b / gcd(a, b), y = y0 - k * a / gcd(a, b)
        (k ∈ Z)
        
    } 

*/

}

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);

    int t;
    t = 1;
    while (t--) {
        solve();
    }

    return 0;
}

参考

  • [1]: https://zhuanlan.zhihu.com/p/651098768
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值