找机会填F题的坑w
C - Count Order
#题意简述
给出两个排列$P$与$Q$($2≤N≤8$),求出它们字典序之差的绝对值。
#简要做法
法1:观察到N很小,可以直接求暴力全排列求字典序。这里补上自己之前不会用的stl做法
Code1
 | 
 | 
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
 | 
 | 
 | 
 | 
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
 | 
 | 
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
 | 
 | 
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; | 
} |