AtCoder Beginner Contest 228(A-Ex)

A - On and Off (atcoder.jp)

        (1)题意

                高桥每天在S点钟打开他房间的灯,并在T点钟关灯,指示灯亮起时,日期可能会发生改变,判断是否在X点过后30分时亮着。

        (2)思路

                直接模拟即可。

        (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;
void solve()
{
    int s,t,x;
    cin >> s >> t >> x;
    for(int i = s;i != t;i ++) {
        if(i == 24) i = 0;
        if(x == i) {
            cout << "Yes";
            return;
        }
    }
    cout << "No";
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    // cin >> T;
    while(T --) solve();
    return 0;
}

B - Takahashi's Secret (atcoder.jp)

        (1)题意

                高桥有N个朋友,刚开始x这个朋友这个人会知道这个秘密,然后每个人在第i个位置会告诉p[i]这个位置的人高桥的秘密,问最后高桥的朋友会有多少知道秘密。

        (2)思路

                对于X这个位置,一直往p[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 = 2e5 + 10;
int p[N],vis[N];
void solve()
{
    int n,x;
    cin >> n >> x;
    rep(i,1,n) cin >> p[i];
    int c = 0;
    while(!vis[x]) {
        vis[x] = true;
        c ++;
        x = p[x];
    }
    cout << c;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    // cin >> T;
    while(T --) solve();
    return 0;
}

C - Final Day (atcoder.jp)

        (1)题意

                N名学生参加四天考试,每天考试总分300分,现在前三天考试已经结束,让你预测每个学生的总分的排名是否是Top K。

        (2)思路

                直接把前三门成绩全部相加,然后排个序,对于每个人直接+300看能否达到Top K即可。

        (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;
ll p[N],z[N];
void solve()
{
    int n,k;
    cin >> n >> k;
    int x;
    rep(i,1,n) {
        rep(j,1,3) {
            cin >> x;
            p[i] += x;
        }
        z[i] = p[i];
    }
    sort(z + 1,z + 1 + n);
    rep(i,1,n) {
        int idx = upper_bound(z + 1,z + 1 + n,p[i] + 300) - z;
        if(n + 1 - idx < k) cout << "Yes\n";
        else 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;
}

D - Linear Probing (atcoder.jp)

        (1)题意

                        有一个长为2^20的序列,初始全部位置为-1,对于第Q个查询,会有两种类型。

第一种类型,定义一个Xi,若Axi%N这个位置不为-1,则往下找到第一个不为-1的数组,把那个位置值设置为Xi。第二种类型就是输出低Xi这个位置值是多少。

        (2)思路

                        用一个set维护一下每个为-1的位置,对于第一个类型的查询,我们直接二分一下那个位置即可。

        (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 = 2e6 + 10;
ll v[N];
void solve()
{
    set<int> st;
    int n = (1 << 20) - 1;
    rep(i,0,n) {
        v[i] = -1;
        st.insert(i);
    }
    int q;
    n ++;
    cin >> q;
    while(q --) {
        ll ti,xi;
        cin >> ti >> xi;
        if(ti == 2) cout << v[xi % n] << '\n';
        else {
            int cur = xi % n;
            auto it = st.lower_bound(cur);
            if(it != st.end()) {
                v[*it] = xi;
                st.erase(it);
            }
            else {
                it = st.lower_bound(0);
                v[*it] = xi;
                st.erase(it);
            }
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    // cin >> T;
    while(T --) solve();
    return 0;
}

E - Integer Sequence Fair (atcoder.jp)

        (1)题意

                计算M^{K^N}

        (2)思路

                考虑先计算上面部分的指数K^N,若不要对下面M产生影响,我们可以使用欧拉降幂,对于K^N进行快速幂取模998244352即可,然后用下面的再计算一次快速幂取模998244353即可。

        (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;
const ll mod = 998244353;
using i128 = __int128;
ll ksm(ll a,ll b,ll p)
{
    if(!a) return 0;
    i128 rs = 1;
    while(b) {
        if(b & 1) rs = rs % p * a % p;
        a = (i128)a % p * a % p;
        b >>= 1;
    }
    return (ll)rs;
}
void solve()
{
    ll n,k,m;
    cin >> n >> k >> m;
    k %= (mod - 1),m %= (mod);
    ll z = ksm(k,n,mod - 1);
    cout << ksm(m,z,mod) << '\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 - Stamp Game (atcoder.jp)

        (1)题意

                给你个H*W的矩形,每个格子里面有一个数,问你选择一个H1*W1的矩形去掉一个H2*W2的矩形后,最多能取多大的值。

        (2)思路

                二维RMQ问题,因此直接上二维ST即可。

        (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 = 1003;
ll s[N][N],a[N][N],val[N][N];

ll dp[N][N][11][11];
int num[N];
void rmq(int n, int m){
    int nn = log2(n), mm = log2(m);
    for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) dp[i][j][0][0] = val[i][j];
    for(int ii=0; ii<=nn; ii++)
    for(int jj=0; jj<=mm; jj++)
    if(ii + jj) {
        for(int i=1; i+(1<<ii)-1 <= n; i++)
        for(int j=1; j+(1<<jj)-1 <= m; j++) {
            if(ii) dp[i][j][ii][jj] = max(dp[i][j][ii-1][jj], dp[i+(1<<(ii-1))][j][ii-1][jj]);
            else dp[i][j][ii][jj] = max(dp[i][j][ii][jj-1], dp[i][j+(1<<(jj-1))][ii][jj-1]);
        }
    }
}
ll query(int x1, int y1, int x2, int y2){
    // ll res = 0;
    // rep(i,x1,x2) rep(j,y1,y2) {
    //     res = max(res,val[i][j]);
    // }
    // return res;
    int k1 = log2(x2-x1+1); //num[x2-x1+1];
    int k2 = log2(y2-y1+1); //num[y2-y1+1];
    x2 = x2 - (1<<k1) + 1;
    y2 = y2 - (1<<k2) + 1;
    ll ans1 = max(dp[x1][y1][k1][k2], dp[x1][y2][k1][k2]);
    ll ans2 = max(dp[x2][y1][k1][k2], dp[x2][y2][k1][k2]);
    return max(ans1, ans2);
}
ll get(int x1,int y1,int x2,int y2)
{
    return s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1];
}
void solve()
{
    int n,m,h1,h2,w1,w2;
    cin >> n >> m >> h1 >> w1 >> h2 >> w2;
    h2 = min(h2,h1),w2 = min(w2,w1);
    rep(i,1,n) {
        rep(j,1,m) {
            cin >> a[i][j];
            s[i][j] = s[i - 1][j] + s[i][j - 1] + a[i][j] - s[i - 1][j - 1];
        }
    }
    rep(i,h2,n) rep(j,w2,m) {
        val[i][j] = get(i - h2 + 1,j - w2 + 1,i,j);
    }
    rmq(n,m);
    ll Ans = 0;
    rep(i,h1,n) rep(j,w1,m) {
        ll s1 = get(i - h1 + 1,j - w1 + 1,i,j);
        ll s2 = query(i - (h1 - h2),j - (w1 - w2),i,j);
        Ans = max(Ans,s1 - s2);
    }
    cout << Ans;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    // cin >> T;
    while(T --) solve();
    return 0;
}

G - Digits on Grid (atcoder.jp)

        (1)题意

                有一个H*W的网格,高桥每次可以把一个棋子移到同一行的某一个位置,青木每次会把棋子移动到同一列的某一个位置,在移动2*N步后,会产生多少个不同的数字序列。

        (2)思路

                对于这些操作来说,实际上是在二分图上走,把行列抽象成不同点,那我们分别对每一步进行dp,定义dp[S]表示目前经过的某一个数字会到达哪些可用行/列,初始化dp[(1 << H) - 1] = 1。

        (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 = 11;
int a[N][N];
vector<int> row[N][N],col[N][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,k;
    cin >> n >> m >> k;
    k <<= 1;
    rep(i,0,n - 1) rep(j,0,m - 1) {
        char c;
        cin >> c;
        a[i][j] = c - '0';
    }
    rep(i,0,n - 1) rep(j,0,m - 1) {
        row[i][a[i][j]].pb(j);
        col[j][a[i][j]].pb(i);
    }
    vector<Mint> dp(1 << n,0);
    dp[(1 << n) - 1] = 1;
    rep(i,1,k) {
        if(i & 1) {
            vector<Mint> ndp(1 << m,0);
            rep(S,1,(1 << n) - 1) {
                if(!dp[S].val()) continue;
                rep(dig,1,9) {
                    int T = 0;
                    rep(j,0,n - 1) {
                        if(!(S >> j & 1)) continue;
                        for(auto p : row[j][dig]) T |= 1 << p;
                    }
                    ndp[T] += dp[S];
                }
            }
            dp = ndp;
        }
        else {
            vector<Mint> ndp(1 << n,0);
            rep(S,1,(1 << m) - 1) {
                if(!dp[S].val()) continue;
                rep(dig,1,9) {
                    int T = 0;
                    rep(j,0,m - 1) {
                        if(!(S >> j & 1)) continue;
                        for(auto p : col[j][dig]) T |= 1 << p;
                    }
                    ndp[T] += dp[S];
                }
            }
            dp = ndp;
        }
    }
    Mint Ans = 0;
    for(int i = 1;i < (1 << n);i ++) {
        Ans += dp[i];
    }
    cout << Ans;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    // cin >> T;
    while(T --) solve();
    return 0;
}

H - Histogram (atcoder.jp)

        (1)题意

                给你两个长度为N的整数序列A和C,你可以操作任意次,每次选择一个整数i,使得A[i] + 1,花费Ci元,完成操作后你需要支付K*X元,K是A元素中不同值得个数。

        (2)思路

                我们对于每一个序列按A为第一关键字排序。考虑某一段数把前面得都变成当前位置得值,那么这个题就转换成了把这个序列划分成多少段得问题。

                因此考虑dp[i]表示把前i段划分好后,最少需要花费多少元? 

                dp[i] = dp[j] + \sum_{k = j + 1}^{i}(a[i] - a[k]) * c[k] + x

                化简一下

                dp[i] = dp[j] + a[i] * \sum_{k = j + 1}^{i}c[k] - \sum_{k = j + 1} ^ {i}a[k] * c[k] + x   

                dp[i] = dp[j] + a[i] * (sumC[i] - sumC[j]) - sumCA[i] + sumCA[j] + x                          考虑前缀和维护sumC和sumCA,对于有j得项抽出来。

                设dp[j] + sumCA[j]为b     -sumC[j]为k

                发现这个东西可以用斜率优化掉,因此直接上李超树即可。

     (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;
namespace OY {
    struct LichaoSegLine {
        double m_k, m_b;
        LichaoSegLine() = default;
        LichaoSegLine(double k, double b) : m_k(k), m_b(b) {}
        double calc(int i) const { return m_k * i + m_b; }
    };
    template <typename Line>
    struct LichaoSegLess {
        bool operator()(const Line &x, const Line &y, int i) const { return x.calc(i) < y.calc(i); }
    };
    template <typename Line = LichaoSegLine, typename Compare = LichaoSegLess<Line>>
    struct LichaoSegTree {
        struct LineNode {
            Line m_line;
            LineNode *m_lchild, *m_rchild;
            LineNode(Line line, LineNode *lchild = nullptr, LineNode *rchild = nullptr) : m_line(line), m_lchild(lchild), m_rchild(rchild) {}
        };
        Line m_defaultLine;
        uint64_t m_length;
        Compare m_comp;
        LineNode *m_root;
        void _clear(LineNode *p) {
            // if (p->m_lchild) _clear(p->m_lchild);
            // if (p->m_rchild) _clear(p->m_rchild);
            // delete p;
        }
        LichaoSegTree(uint64_t length = 0, Compare comp = Compare(), Line defaultLine = Line()) : m_comp(comp), m_defaultLine(defaultLine), m_root(nullptr) { resize(length); }
        ~LichaoSegTree() {
            if (m_root) _clear(m_root);
        }
        void resize(uint64_t length) {
            if (m_root) _clear(m_root);
            m_root = (m_length = length) ? new LineNode(m_defaultLine, nullptr, nullptr) : nullptr;
        }
        void add(uint64_t left, uint64_t right, const Line &line) {
            if (right >= m_length) right = m_length - 1;
            if (left > right) return;
            auto dfs = [&](auto self, LineNode *&cur, uint64_t lbound, uint64_t rbound, Line line) -> void {
                if (!cur) cur = new LineNode(m_defaultLine, nullptr, nullptr);
                if (uint64_t mid = (lbound + rbound) / 2; left <= lbound && right >= rbound) {
                    if (m_comp(cur->m_line, line, mid)) std::swap(cur->m_line, line);
                    if (lbound < rbound)
                        if (m_comp(cur->m_line, line, lbound))
                            self(self, cur->m_lchild, lbound, mid, line);
                        else if (m_comp(cur->m_line, line, rbound))
                            self(self, cur->m_rchild, mid + 1, rbound, line);
                } else {
                    if (left <= mid) self(self, cur->m_lchild, lbound, mid, line);
                    if (right > mid) self(self, cur->m_rchild, mid + 1, rbound, line);
                }
            };
            dfs(dfs, m_root, 0, m_length - 1, line);
        }
        Line query(uint64_t i) const {
            Line res(m_defaultLine);
            auto dfs = [&](auto self, LineNode *cur, uint64_t lbound, uint64_t rbound) -> void {
                if (!cur) return;
                if (m_comp(res, cur->m_line, i))
                    res = cur->m_line;
                if (lbound == rbound) return;
                if (uint64_t mid = (lbound + rbound) / 2; i <= mid)
                    self(self, cur->m_lchild, lbound, mid);
                else
                    self(self, cur->m_rchild, mid + 1, rbound);
            };
            dfs(dfs, m_root, 0, m_length - 1);
            return res;
        }
    };
    template <typename Line = LichaoSegLine, typename Compare = LichaoSegLess<Line>>
    LichaoSegTree(uint64_t = 0, Compare = Compare(), Line = Line()) -> LichaoSegTree<Line, Compare>;
}
using i128 = __int128;
struct Line {
    ll k,b;
    ll calc(ll i) const {
        return i * k + b;
    }
};
 struct compare {
    bool operator()(const Line &x, const Line &y, i128 i) const {
        ll x_val = x.calc(i), y_val = y.calc(i);
        return x_val > y_val;
    }
};
const uint64_t inf = 1e12;
pair<ll,ll> e[N];
ll sumC[N],sumCA[N];
ll dp[N];
void solve()
{
    int n,x;
    cin >> n >> x;
    //dp[i]:前i个的最小花费
    //dp[i] = dp[j] + k:[j + 1,i](a[i] - a[k])*c[k] + x
    //dp[i] = dp[j] + a[i] * k:[j + 1,i]c[k] - k:[j + 1,i]:a[k] * c[k] + x
    //dp[i] = dp[j] + a[i] * (sumC[i] - sumC[j]) - sumCA[i] + sumCA[j] + x
    //dp[i] = dp[j] + a[i] * sumC[i] - a[i] * sumC[j] - sumCA[i] + sumCA[j] + x
    //设dp[j] + sumCA[j]为b -sumC[j]为k
    //则dp[i] = b + a[i] * sumC[i] - a[i] * k - sumCA[i] + x
    //根据a[i] query一个最小的值进行转移即可
    OY::LichaoSegTree tr(1000005,compare(),Line{0,inf});
    for(int i = 1;i <= n;i ++) {
        cin >> e[i].fi >> e[i].se;
    }
    sort(e + 1,e + 1 + n);
    for(int i = 1;i <= n;i ++) {
        sumC[i] = sumC[i - 1] + e[i].se;
        sumCA[i] = sumCA[i - 1] + 1ll * e[i].fi * e[i].se;
    }
    tr.add(0,inf,{0,0});
    for(int i = 1;i <= n;i ++) {
        dp[i] = 1ll * e[i].fi * sumC[i] + tr.query(e[i].fi).calc(e[i].fi) - sumCA[i] + x;
        tr.add(0,inf,{-sumC[i],dp[i] + sumCA[i]});
    }
    cout << dp[n] << '\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、付费专栏及课程。

余额充值