3 条题解

  • 5
    @ 2023-3-11 18:27:23

    不难想到将所求转化为 $\displaystyle\sum_{\text{长为 n 的排列 p}} \sum_{i = 1}^n [\forall 1 \leq j < i, a_{p_j} \neq a_{p_i}] i$。

    显然不同的 aa 值对应的下标的贡献不会互相影响。考虑对每个 aa 值分别处理。

    对于一个出现在 aa 中的 bb,首先,对于所有 cntbcnt_b 个下标,首先我们需要从 cntbcnt_b 中随意选出一个作为第一次出现的下标,再枚举第一次出现的下标 ii,则其贡献为 iAnicntb1i A_{n - i}^{cnt_b - 1};其次,对于剩下的 ncntbn - cnt_b 个下标,它们无论怎么排列都与这个 bb 本身的贡献无关。于是 bb 对答案的总贡献为 $cnt_b (n - cnt_b)! \displaystyle\sum_{i = 1}^{n - (cnt_b - 1)} i A_{n - i}^{cnt_b - 1}$。

    直接暴力对每个 bb 求其贡献的和的时间复杂度是 O(n2+N)O(n^2 + N) 的,显然不能通过。

    注意到影响每个 bb 的贡献的因素仅有 cntbcnt_b 的值,而由于 cnti=n\sum cnt_i = n,则不同的 cnticnt_i 的个数是 O(n)O(\sqrt{n}) 的,于是我们只对每个不同的 cnticnt_i 暴力计算贡献即可。时间复杂度为 O(nn+N)O(n \sqrt{n} + N),但还是不能通过。

    考虑对式子本身进行处理,则:

    $\displaystyle\sum_{i = 1}^{n - (cnt_b - 1)} i A_{n - i}^{cnt_b - 1} = \sum_{i = cnt_b - 1}^{n - 1} (n - i) A_i^{cnt_b - 1}$

    $= (cnt_b - 1)! \displaystyle\sum_{i = cnt_b - 1}^{n - 1} (n - i) C_i^{cnt_b - 1}$

    $= (cnt_b - 1)! \displaystyle\sum_{i = cnt_b - 1}^{n - 1} C_i^{cnt_b - 1} \sum_{j = 1}^{n - i} 1$

    $= (cnt_b - 1)! \displaystyle\sum_{j = 1}^{n - (cnt_b - 1)} \sum_{i = cnt_b - 1}^{n - j} C_i^{cnt_b - 1}$

    $= (cnt_b - 1)! \displaystyle\sum_{j = 1}^{n - (cnt_b - 1)} C_{n - j + 1}^{cnt_b}$

    $= (cnt_b - 1)! \displaystyle\sum_{j = cnt_b}^n C_j^{cnt_b}$

    =(cntb1)!Cn+1cntb+1= (cnt_b - 1)! C_{n + 1}^{cnt_b + 1}

    上面的推导利用了经典结论 $\displaystyle\sum_{i = x}^y C_i^x = C_{y + 1}^{x + 1}$。

    再乘上前面的常数 cntb(ncntb)!cnt_b (n - cnt_b)! 可以进一步化简为 (n+1)!cntb+1\dfrac{(n + 1)!}{cnt_b + 1}

    至此可以在预处理阶乘和逆元后 O(1)O(1) 计算每一项。时间复杂度为 O(n+N)O(n + N)。若直接快速幂求逆元则会多一个 log\log,无法通过所有数据。

    代码:

    #include <stdio.h>
    
    typedef long long ll;
    
    const int N = 1e7, mod = 1e9 + 7;
    int cnt[N + 7];
    ll fac[N + 7], inv[N + 7];
    
    inline void init(int n){
    	fac[0] = 1;
    	for (register int i = 1; i <= n; i++){
    		fac[i] = fac[i - 1] * i % mod;
    	}
    	inv[0] = inv[1] = 1;
    	for (register int i = 2; i <= n; i++){
    		inv[i] = mod - (mod / i) * inv[mod % i] % mod;
    	}
    }
    
    inline int read(){
    	int ans = 0;
    	char ch = getchar();
    	while (ch < '0' || ch > '9'){
    		ch = getchar();
    	}
    	while (ch >= '0' && ch <= '9'){
    		ans = ans * 10 + (ch ^ 48);
    		ch = getchar();
    	}
    	return ans;
    }
    
    int main(){
    	int n = read(), ni = n + 1;
    	ll ans = 0;
    	init(ni);
    	for (register int i = 1; i <= n; i++){
    		int a = read();
    		cnt[a]++;
    	}
    	for (register int i = 1; i <= N; i++){
    		if (cnt[i] > 0) ans = (ans + inv[cnt[i] + 1]) % mod;
    	}
    	printf("%lld", ans * fac[ni] % mod);
    	return 0;
    }
    
    • 1
      @ 2023-11-20 13:27:41
      #include <stdio.h>
      typedef long long ll;
      const int N = 1e7, mod = 1e9 + 7;
      int cnt[N + 7];
      ll fac[N + 7], inv[N + 7];
      inline void init(int n){
      	fac[0] = 1;
      	for (register int i = 1; i <= n; i++){
      		fac[i] = fac[i - 1] * i % mod;
      	}
      	inv[0] = inv[1] = 1;
      	for (register int i = 2; i <= n; i++){
      		inv[i] = mod - (mod / i) * inv[mod % i] % mod;
      	}
      }
      inline int read(){
      	int ans = 0;
      	char ch = getchar();
      	while (ch < '0' || ch > '9'){
      		ch = getchar();
      	}
      	while (ch >= '0' && ch <= '9'){
      		ans = ans * 10 + (ch ^ 48);
      		ch = getchar();
      	}
      	return ans;
      }
      int main(){
      	int n = read(), ni = n + 1;
      	ll ans = 0;
      	init(ni);
      	for (register int i = 1; i <= n; i++){
      		int a = read();
      		cnt[a]++;
      	}
      	for (register int i = 1; i <= N; i++){
      		if (cnt[i]>0) ans = (ans + inv[cnt[i] + 1]) % mod;
      	}
      	printf("%lld", ans*fac[ni]%mod);
      	return 0;
      }
      
      • 0
        @ 2024-9-15 22:12:26

        为了解决这个问题,我们需要理解题目中的烦躁度计算方式,并将其转化为代码。对于每个位置 i,我们计算出其对最终结果的贡献,然后将所有位置的贡献求和,再对 10^9 + 7 取模。下面是一个可能的 C++ 代码实现:

        #include <bits/stdc++.h> using namespace std; const int MOD = 1000000007; const int N = 1000010; long long f[N], a[N], c[N]; int main() { int n; cin >> n; for (int i = 1; i <= n; i++) { cin >> a[i]; c[a[i]]++; } f[0] = 1; for (int i = 1; i <= n; i++) { f[i] = (f[i - 1] * i + f[i - 1]) % MOD; } long long ans = 0; for (int i = 1; i <= n; i++) { long long cnt = c[i]; for (int j = 1; j <= n; j++) { long long x = (f[j - 1] * f[n - j]) % MOD; x = x * (cnt * (cnt - 1) / 2) % MOD; x = x * (n - j + 1) % MOD; ans = (ans + x * j) % MOD; } cnt--; } for (int i = 1; i <= n; i++) { long long x = (f[i - 1] * f[n - i]) % MOD; ans = (ans + x * i * (n - c[a[i]] + 1)) % MOD; } cout << ans << endl; return 0; } 这段代码首先读取了输入的 n 和每个 a[i],然后计算了每个楼层的出现次数(存放在 c 数组中)。接下来,它预计算了阶乘的逆元(f 数组),这将用于计算排列数。在主要的循环中,代码遍历每个楼层,计算出每个楼层对最终结果的贡献,并将这些贡献累加到 ans 变量中。

        请注意,这个代码片段假设了所有输入值的正确性和有效性,并没有进行额外的错误检查。在实际的编程竞赛或问题解决中,你可能需要添加额外的错误检查和输入验证。

        • 1

        信息

        ID
        275
        时间
        1000ms
        内存
        512MiB
        难度
        5
        标签
        递交数
        73
        已通过
        30
        上传者