1. 程式人生 > 其它 >AtCoder Beginner Contest 221 A~E 題解

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;
}