0%

AtCoder Beginner Contest 150题解

找机会填F题的坑w

C - Count Order

#题目链接

#题意简述

给出两个排列$P$与$Q$($2≤N≤8$),求出它们字典序之差的绝对值。

#简要做法

法1:观察到N很小,可以直接求暴力全排列求字典序。这里补上自己之前不会用的stl做法

Code1
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10;
int n;
int p[maxn] , q[maxn] , t[maxn];
inline void init() {
    for(register int i = 1; i <= n; ++i)
        t[i] = i;
}
inline bool check(int *a) {
    for(register int i = 1; i <= n; ++i)
        if(a[i] != t[i]) return false;
    return true;
}
int main() {
    cin >> n;
    for(register int i = 1; i <= n; ++i) cin >> p[i];
    for(register int i = 1; i <= n; ++i) cin >> q[i];
    init();
    int cnt1 = 0 , cnt2 = 0;
    while(!check(p)) {
        cnt1++;
        next_permutation(t + 1 , t + n + 1);
    }
    init();
    while(!check(q)) {
        cnt2++;
        next_permutation(t + 1 , t + n + 1);
    }
    cout << abs(cnt1 - cnt2) << endl;
    return 0;
}

法2:若N比较大,则可以用康托展开求序:

​ 康托展开:在排列$P$中,对第$k$位而言,使前$k-1$位相同,则其一定大于$(小于p[k]且尚未出现的数字个数)\cdot(n-k)!$,因此答案为$\sum_{i=1}^nC(p[i])\cdot(n-i)! + 1$。其中C(p[i])为排列中小于p[i]但是尚未出现的数字个数。时间复杂度$O(N^2)$,用树状数组优化为$O(NlogN)$ 反正不知道比n!复杂度高到哪里去了

Code2
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define ll long long
#define lowbit(x) (x&-x) 
using namespace std;
const int maxn = 10;
int n;
int p[maxn] , q[maxn] , tree[maxn];
ll f[maxn];
void init() {
    f[0] = 1;
    for(register int i = 1; i <= n; ++i)
        f[i] = i * f[i - 1];
}
inline void update(int x) {
    for(register int i = x; i <= n; i += lowbit(i)) 
        tree[i] += 1;
}
inline ll query(int x) {
    ll ret = 0;
    for(register int i = x; i; i -= lowbit(i)) 
        ret += tree[i];
    return ret;
}
ll cantor(int *a) {
    memset(tree , 0 , sizeof(tree));
    ll ret = 0;
    for(register int i = 1; i <= n; ++i) {
        ll cnt = query(a[i]); //求出已经有多少个小于a[i]的数已经出现了 
        update(a[i]);
        ret += (a[i] - cnt - 1) * f[n - i];
    }
    return ret + 1; //由于字典序从1开始,所以加1 
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n;
    for(register int i = 1; i <= n; ++i) cin >> p[i];
    for(register int i = 1; i <= n; ++i) cin >> q[i];
    init();
    ll a = cantor(p) , b = cantor(q);
    cout << abs(a - b) << endl;
    return 0;
}

D - Semi Common Multiple

#题目链接

#题意简述

给定一个偶数序列$A(a_i≤10^9)$,长度为$N(N≤10^5)$。试求$1至M(M≤10^9)$中,有多少个整数X可以由任意序列A中的数以$X=a_k\cdot(p+0.5)$表现出来。

#简要做法

由于$a_k$都是偶数,可以整理得$X=\frac{a_k}{2}\cdot(2p+1)$,即X为一切$\frac{a_k}{2}$的奇数倍。因此求出序列$A$的一半的最小公倍数,如果其不是所有元素一半的奇数倍或者大于M,则不合法。所有满足条件的X一定是X的奇数倍,所以答案为M/X/2,如果M/X为奇数则答案再自加1。

Code
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll n , m;
const int maxn = 1e5 + 5;
ll a[maxn];
inline ll gcd(ll a , ll b) {
    return b == 0 ? a : gcd(b , a % b);
}
inline ll lcm(ll a , ll b) {
    return a / gcd(a , b) * b;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n >> m;
    ll minx;
    bool valid = 1;
    for(register int i = 1; i <= n; ++i) {
        cin >> a[i]; a[i] >>= 1; 
        if(i == 1) minx = a[1];
        minx = lcm(minx , a[i]);
        if(minx > m) valid = 0;
    }
    for(register int i = 1; i <= n; ++i)
        if(!(minx / a[i] & 1)) {
            valid = 0;
            break;
        }
    if(!valid)
        cout << 0 << endl;
    else {
        ll ans = m / minx;
        if(ans & 1) ans++;
        ans /= 2;
        cout << ans << endl;
    }
    return 0;
}

E - Change a Little Bit

#题目链接

Problem Statement

For two sequences $S$ and $T$ of length $N$ consisting of 0 and 1, let us define $f(S,T)$ as follows:

  • Consider repeating the following operation on $S$ so that $S$ will be equal to $T$. $f(S,T)$ is the minimum possible total cost of those operations.
    • Change $S_i$ (from 0 to 1 or vice versa). The cost of this operation is $D×C_i$, where $D$ is the number of integers $j$ such that $S_j≠T_j(1≤j≤N)$just before this change.

There are $2N×(2N−1)$pairs $(S,T)$ of different sequences of length $N$ consisting of $0$ and $1$. Compute the sum of $f(S,T)$ over all of those pairs, modulo $(10^9+7)$.

Constraints
  • $1≤N≤2×10^5 1≤N≤2×10^5$
  • $1≤C_i≤10^9 1≤C_i≤10^9$
  • All values in input are integers.

#简要做法

由排序不等式可知,想要有最小的代价,必须最后处理大的数。假设一对完全不同的$(S,T)$,令$C_i$递减,则$f(S,T)=\sum_{i=1}^NC_i\cdot i$。现在已知有$2^{2N}$对$(S,T)$,对于任意一个$C_i$当它和对应的一位不同时才会被计算上,它前面的$(i-1)$为各自有$\frac{1}{2}$的概率和已有序列不同,因此期望计算$\frac{i-1}{2}$次,合计期望$\frac{i+1}{2}$次,又该位本身有$\frac{1}{2}$的概率被计算,因此合计期望为$\frac{i+1}{4}$次,可推导出答案为$2^{2N-2}\cdot\sum_{i=1}^NC_i\cdot(i+1))$。

Code
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn = 2e5 + 5 , mod = 1e9 + 7;
int n;
ll c[maxn];
ll fpow(ll x , ll k) {
    ll ans = 1 , base = x;
    while(k) {
        if(k & 1) ans = ans * base % mod;
        base = base * base % mod;
        k >>= 1;
    }
    return ans;
}
inline bool cmp(ll a , ll b){ return a > b; }
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n;
    for(register int i = 1; i <= n; ++i) cin >> c[i];
    sort(c + 1 , c + n + 1 , cmp);
    ll ans = 0;
    for(register int i = 1; i <= n; ++i) 
        ans = (ans + c[i] * (i + 1) % mod) % mod;
    ans = ans * fpow(2 , 2 * n - 2) % mod;
    cout << ans << endl;
    return 0;
}