AtCoder Beginner Contest 319(D-G)

21 篇文章 0 订阅
10 篇文章 0 订阅
文章介绍了AtCoder编程比赛中的三个题目,涉及二维空间中单词排版的最小宽度计算,公交路线中到达时间的最短路径查询,以及在树上战斗中利用药剂和战斗力升级的问题。解题策略包括二分查找、状态转移和动态规划。
摘要由CSDN通过智能技术生成

D.Tasks - AtCoder Beginner Contest 319

        (1)题意

                给你一个M行得框框和N个单词,每个单词有一个宽度,每个单词之间应该用一个空格隔开,首位单词不需要,问至少需要多宽才能使得单词不会超过M行。

        (2)解题思路

                直接二分宽度,然后check即可。

        (3)代码实现

#include <bits/stdc++.h>
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) (x).begin(),(x).end()
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
int L[N];
int n,m;
bool check(ll k)
{
    int cnt = 0;
    ll cur = 0;
    rep(i,1,n) {
        if(k < L[i]) {
            return false;
        }
    }
    rep(i,1,n) {
        if(cur - L[i] - 1 >= 0) {
            cur -= L[i] + 1;
        }
        else {
            cur = k - L[i];
            cnt ++;
        }
    }
    // cout << "???" << k << '\n';
    return cnt <= m;
}
void solve()
{
    cin >> n >> m;
    for(int i = 1;i <= n;i ++) {
        cin >> L[i];
    }
    ll l = 1,r = 1e18;
    while(l <= r) {
        ll mid = (l + r) >> 1;
        if(check(mid)) r = mid - 1;
        else l = mid + 1;
    }
    cout << l << endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    // cin >> T;
    while(T --) solve();
    return 0;
}

E - Bus Stops (atcoder.jp)

        (1)题意

                A要从自己家到B家,中间得路段构成是A先走一段路到达站点1,然后做公交到站点n,最后走一段路到B家,每一个站点得公交车会在P[i]倍数得时间发车,需要T[i]得时间才能到达下一个站,现在有q个询问,每个询问给定你起始时间,问最后达到B家得最小时间是多少。

        (2)解题思路

                考虑每一个P[i]得倍数都会有一些余数,不妨把他们得最小公倍数求出来,然后预处理出0-最小公倍数得起始时间需要花费多少时间到达B家。

        (3)代码实现

#include <bits/stdc++.h>
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) (x).begin(),(x).end()
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
int p[N],t[N];
ll Ans[N];
int n,x,y;
ll calc(ll st)
{
    st += x;
    for(int i = 1;i < n;i ++) {
        int z = st % p[i];
        if(!z) z = p[i];
        st += p[i] - z;
        st += t[i];
    }
    return st + y;
}
void solve()
{
    cin >> n >> x >> y;
    set<int> pt;
    for(int i = 1;i < n;i ++) {
        cin >> p[i] >> t[i];
        pt.insert(p[i]);
    }
    int cur = 0;
    for(auto x : pt) {
        if(!cur) cur = x;
        else cur = cur * x / __gcd(cur,x);
    }
    for(int i = 0;i < cur;i ++) {
        Ans[i] = calc(i);
    }
    int q;
    cin >> q;
    for(int i = 1;i <= q;i ++) {
        ll v;
        cin >> v;
        int p = v % cur;
        cout << v - p + Ans[p] << '\n';
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    // cin >> T;
    while(T --) solve();
    return 0;
}

F - Fighter Takahashi (atcoder.jp)

        (1)题意

                你在一颗树上战斗,起初你的战斗力是1,树上每个点要么是药剂,要么是怪兽,如果是怪兽,你的战斗力大于等于他就可以击败他获取奖励战斗力,如果是药剂你可以使你得战斗力翻g[i]倍,现在问你是否可以从1号节点战败所有怪兽。

        (2)解题思路

                首先,若是你当前得实力够打怪兽,那么一定是先打得,如果不够了才考虑用药剂。那么一个很明显得思路是,确定一个吃药得顺序,然后bfs一遍看看能不能打完所有怪兽,那么时间复杂度显然是不太行得。所以我们考虑使用状压dp优化这个枚举吃药顺序得过程,状态dp[i]表示吃了i这些药最高能到得战斗力是多少,最后判一下是否大于等于最高战斗力得怪兽即可。

        (3)代码实现

#include <bits/stdc++.h>
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) (x).begin(),(x).end()
using namespace std;
using ll = long long;
const int N = 505;
int t[N],s[N],g[N],p[N];
vector<int> e[N];
ll dp[1<<(10)];
void solve()
{
    int n,tot = 0;
    cin >> n;
    vector<int> med,pos(n + 1,0);
    int master = 0;
    rep(i,2,n) {
        cin >> p[i] >> t[i] >> s[i] >> g[i];
        if(t[i] == 2) {
            med.pb(i);
            pos[i] = sz(med) - 1;
            tot ++;
        }
        master = max(master,s[i]);
        e[p[i]].pb(i);
    }
    memset(dp,-1,sizeof(dp));
    //dp[i]:表示吃了这些药能达到最大得能量值
    dp[0] = 1;
    rep(i,0,(1<<tot)-1) {
        if(dp[i] != -1) {
            priority_queue<pair<ll,int>,vector<pair<ll,int>>,greater<pair<ll,int>>> q;
            q.push({-1,1});
            vector<bool> used(sz(med),false);
            int cnt = 0;
            ll S = 0;
            while(!q.empty()) {
                auto [val,ver] = q.top();
                q.pop();
                if(val > dp[i]) break;
                cnt ++;
                if(t[ver] == 2 && !(i >> pos[ver] & 1)) {
                    used[pos[ver]] = true;
                    continue;
                }
                if(t[ver] == 1) {
                    dp[i] += g[ver];
                    S += g[ver];
                }
                for(auto v : e[ver]) {
                    if(t[v] == 2) q.push({-1,v});
                    else q.push({s[v],v}); 
                }
            }
            // cout << i << ' ' << dp[i] << '\n';
            if(dp[i] >= master || cnt == n) {
                cout << "Yes" << '\n';
                return;
            }
            rep(j,0,tot-1) {
                if(used[j]) {
                    dp[i | (1 << j)] = max(dp[i | (1 << j)],dp[i] * g[med[j]] - S);
                }
            }
        }
    }
    cout << "No" << '\n';
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    // cin >> T;
    while(T --) solve();
    return 0;
}

G - Counting Shortest Paths (atcoder.jp)

        (1)题意

                给你一个完全图,删除一些边后,问你是否能从1-N,如果能,请给出最短路径数量。

        (2)解题思路

               P1. 首先对于最短路径,我们考虑01bfs得特性,一旦有一个点进入了则不会再进入一次,因此我们可以用一个set维护一下未进入得点集,每次对于一个进入得点,扫描一下未进入得点集,若这个点和进入得点之间得边未删除,则可以把这个点加入队列,这样就可以很快得求得最短路径。

               P2.现在我们需要考虑路径数量得问题了,考虑将图分成如下形式。

                考虑dp[i],表示i为最短路径上的点的方案数是多少。那么显然对于点集为2的点dp[i]等价于所有点集为1的点的方案和-i不能到达的点集为1的点的方案,依次类推求出dp[n]即可。

        (3)代码实现

#include <bits/stdc++.h>
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) (x).begin(),(x).end()
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
set<int> e[N],g[N],pos;
int dis[N];
using i64 = long long;

constexpr int P = 998244353;
// assume -P <= x < 2P
int Vnorm(int x) {
    if (x < 0) {
        x += P;
    }
    if (x >= P) {
        x -= P;
    }
    return x;
}
template<class T>
T power(T a, i64 b) {
    T res = 1;
    for (; b; b /= 2, a *= a) {
        if (b % 2) {
            res *= a;
        }
    }
    return res;
}
struct Mint {
    int x;
    Mint(int x = 0) : x(Vnorm(x)) {}
    Mint(i64 x) : x(Vnorm(x % P)) {}
    int val() const {
        return x;
    }
    Mint operator-() const {
        return Mint(Vnorm(P - x));
    }
    Mint inv() const {
        assert(x != 0);
        return power(*this, P - 2);
    }
    Mint &operator*=(const Mint &rhs) {
        x = i64(x) * rhs.x % P;
        return *this;
    }
    Mint &operator+=(const Mint &rhs) {
        x = Vnorm(x + rhs.x);
        return *this;
    }
    Mint &operator-=(const Mint &rhs) {
        x = Vnorm(x - rhs.x);
        return *this;
    }
    Mint &operator/=(const Mint &rhs) {
        return *this *= rhs.inv();
    }
    friend Mint operator*(const Mint &lhs, const Mint &rhs) {
        Mint res = lhs;
        res *= rhs;
        return res;
    }
    friend Mint operator+(const Mint &lhs, const Mint &rhs) {
        Mint res = lhs;
        res += rhs;
        return res;
    }
    friend Mint operator-(const Mint &lhs, const Mint &rhs) {
        Mint res = lhs;
        res -= rhs;
        return res;
    }
    friend Mint operator/(const Mint &lhs, const Mint &rhs) {
        Mint res = lhs;
        res /= rhs;
        return res;
    }
    friend std::istream &operator>>(std::istream &is, Mint &a) {
        i64 v;
        is >> v;
        a = Mint(v);
        return is;
    }
    friend std::ostream &operator<<(std::ostream &os, const Mint &a) {
        return os << a.val();
    }
};
void solve()
{
    int n,m;
    cin >> n >> m;
    for(int i = 1;i <= m;i ++) {
        int u,v;
        cin >> u >> v;
        e[u].insert(v);
        e[v].insert(u);
    }
    for(int i = 2;i <= n;i ++) {
        pos.insert(i);
    }
    memset(dis,0x3f,sizeof(dis));
    dis[1] = 0;
    queue<int> q;
    q.push(1);
    while(!q.empty()) {
        int v = q.front();
        q.pop();
        auto nps = pos;
        for(auto x : nps) {
            if(!e[v].count(x) && pos.count(x)) {
                dis[x] = dis[v] + 1;
                pos.erase(x);
                q.push(x);
            }
        }
    }
    if(dis[n] >= n) {
        cout << -1 << '\n';
        return;
    }
    for(int i = 1;i <= n;i ++) {
        if(dis[i] <= n) g[dis[i]].insert(i);
    }
    vector<Mint> dp(n + 1,0),s(n + 1,0);
    dp[1] = 1;
    for(int i = 0;i < n;i ++) {
        if(i >= 1) {
            for(auto x : g[i]) {
                dp[x] += s[i];
            }
        }
        for(auto x : g[i]) {
            s[i + 1] += dp[x];
            for(auto y : e[x]) {
                if(dis[y] == dis[x] + 1) {
                    dp[y] -= dp[x];
                }
            }
        }
    }
    cout << dp[n];
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    // cin >> T;
    while(T --) solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值