AtCoder Beginner Contest 221 A~E 题解

本场链接:AtCoder Beginner Contest 221

闲话

因为 E 的公式写歪了一点,多做了两小时,吐了.F 看起来像是一个分类讨论,不太想做,如果做了再写吧.

A - Seismic magnitude scales

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
mt19937_64 rng(chrono::high_resolution_clock::now().time_since_epoch().count());

int main()
{
    Angel_Dust;
    int a,b;cin >> a >> b;
    ll res = 1;
    forn(_,1,a - b) res *= 32;
    cout << res << endl;
    return 0;
}

B - typo

注意可以不使用操作,以及操作之后需要还原.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
mt19937_64 rng(chrono::high_resolution_clock::now().time_since_epoch().count());


int main()
{
    Angel_Dust;
    string s,t;cin >> s >> t;
    if(s == t)  return cout << "Yes" << endl,0;
    int n = s.size();
    forn(i,0,n - 2)
    {
        swap(s[i],s[i + 1]);
        if(s == t)  return cout << "Yes" << endl,0;
        swap(s[i],s[i + 1]);
    }

    cout << "No" << endl;
    return 0;
}

C - Select Mul

数字最多有 9 位,那么他的排列最多有 9! 种,每个排列最多有 8 种分割点,所以复杂度上界是 8*9! 的,显然可以直接枚举求出最大值.

但是需要注意.使用 next_permutation 来枚举所有排列之前应该先把整个排列排序,否则会错过比一开始输入的序列更小的排列.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
mt19937_64 rng(chrono::high_resolution_clock::now().time_since_epoch().count());

const int N = 12;
char s[N];

int main()
{
    scanf("%s",s + 1);int n = strlen(s + 1);
    sort(s + 1,s + n + 1);

    ll res = 0;
    do
    {
        forn(i,1,n - 1)
        {
            if(s[1] == '0') continue;
            if(s[i + 1] == '0' && n - i != 1)   continue;
            int L = 0,R = 0;
            forn(j,1,i)         L = L * 10 + s[j] - '0';     
            forn(j,i + 1,n)     R = R * 10 + s[j] - '0';
            res = max(res,1ll * L * R);
        }
    }while(next_permutation(s + 1,s + n + 1));

    printf("%lld\n",res);
    return 0;
}

D - Online games

经典套路:可以把每个覆盖操作看做是两个事件,分别在 \(A_i\) 时刻提供加一,在 \(A_i + B_i\) 时刻减去之前提供的贡献.那么把所有事件按时间排序再维护出当前总共在的人数以及上一位事件的时刻即可求出答案.

当然,这样做事件一个原因就是不能直接维护值域,所以自然也可以将整个值域离散化.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
mt19937_64 rng(chrono::high_resolution_clock::now().time_since_epoch().count());

const int N = 2e5+7;
struct Event
{
    int t,v;
    bool operator<(const Event& r)  const
    {
        return t < r.t;
    }
};
vector<Event> E;
ll ans[N];

int main()
{
    int n;scanf("%d",&n);
    forn(i,1,n)
    {
        int a,b;scanf("%d%d",&a,&b);
        E.push_back({a,1});
        E.push_back({a + b,-1});
    }   

    sort(E.begin(),E.end());reverse(E.begin(),E.end());

    int cur = 0,last = 0;

    while(!E.empty())
    {
        int u = E.back().t;
        ans[cur] += u - last;
        cur += E.back().v;E.pop_back();
        while(!E.empty() && E.back().t == u)    cur += E.back().v,E.pop_back();
        last = u;
    }

    forn(i,1,n) printf("%lld ",ans[i]);puts("");
    return 0;
}

E - LEQ

首先考虑这道题的最本质做法:枚举两个下标 \(j < i\)\(a_j \leq a_i\) 那么这一对元素中间的一段长度为 \(i - j - 1\) 的序列,里面的每个元素只有选和不选两种方案,所以对整个答案的贡献就是 \(2_{i - j - 1}\).

考虑优化:只枚举右端点 \(i\),记 \(ans[i]\) 表示右端点为 \(i\) 的时候的总贡献,则显然 \(ans = \sum ans[i]\).

考虑求单个 \(ans[i]\): \(ans[i] = \sum_{j <i,a_j \leq a_i} 2^{i - j - 1}\).考虑分离 \(j\)\(i\) 使得答案可以快速统计: \(ans[i] = \sum_{j < i,a_j \leq a_i} 2^{i - 1} / \sum_{j < i,a_j \leq a_i} 2^j = \sum_{j < i,a_j \leq a_i} 2^{i - 1} * \sum_{j < i,a_j \leq a_i}2^{-j}\).后面这部分可以使用树状数组维护在某个位置之前的逆元的和,前面直接快速幂即可.如此统计答案即可.

当然,由于需要在 \(a_i\) 处加入逆元的贡献,所以首先需要对 \(\{a\}\)进行离散化.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
mt19937_64 rng(chrono::high_resolution_clock::now().time_since_epoch().count());

const int N = 3e5+7,MOD = 998244353,M = 3e5+7;
vector<int> vals;
int a[N];
ll c[N];

inline int link(int x)
{
    return lower_bound(vals.begin(),vals.end(),x) - vals.begin() + 2;
}

inline ll lowbit(ll x)
{
    return x & -x;
}

ll query(ll x)
{
    ll res = 0;
    for(int i = x;i;i -= lowbit(i)) res = (res + c[i]) % MOD;
    return res;
}

void modify(ll x,ll v)
{
    for(int i = x;i < N;i += lowbit(i)) c[i] = (c[i] + v) % MOD;
}

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

int main()
{
    int n;scanf("%d",&n);
    forn(i,1,n) scanf("%d",&a[i]),vals.push_back(a[i]);
    sort(vals.begin(),vals.end());vals.erase(unique(vals.begin(),vals.end()),vals.end());
    forn(i,1,n) a[i] = link(a[i]);
    
    ll res = 0;
    forn(i,1,n)
    {
        if(i > 1)   res = (res + qpow(2,i - 1,MOD) * query(a[i]) % MOD) % MOD;

        ll dw = qpow(2,i,MOD);
        dw = qpow(dw,MOD - 2,MOD);
        modify(a[i],dw);
    }
    printf("%lld\n",res);
    return 0;
}

posted @ 2021-10-03 09:32  随处可见的阿宅  阅读(72)  评论(0编辑  收藏  举报