1 条题解

  • 0
    @ 2025-8-24 21:40:20

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar BlackHoles
    BlackHoles.

    搬运于2025-08-24 21:40:20,当前版本为作者最后更新于2025-07-14 20:07:01,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    :本题解提供了前置知识以及极其详细的思维过程,并提供了题解区唯一的证明。

    前置知识

    卢卡斯定理,组合数学相关知识。

    正解

    我们对于任意 1in1\leq i\leq n,考虑 ii 对于答案做出的贡献次数。

    我们记 valival_i 表示 ii 对答案的贡献次数,总答案即为 i=1ni×vali\sum_{i=1}^{n}i\times val_i

    易发现,位于中心的数对答案做出的贡献次数越多。于是我们利用贪心思想将大的数放在中间,小的数放在两边。(实现见后方)

    手玩样例,发现四个数对答案的贡献次数为 [1,3,3,1][1,3,3,1],好似杨辉三角组合数。

    构造多组样例发现于是我们得出结论:对于 1in1\leq i\leq nvali=Cn1i1val_i=C_{n-1}^{i-1}

    简略证明:对于 ii,我们发现每次向下一层进行贡献的时候,每个节点将会往左和往右进行贡献(为空的节点自行补齐),发现就是一个杨辉三角。容易证明数字王国最底部的数位于该杨辉三角最后一层中从左往右第 ni+1n-i+1 的位置。根据杨辉三角的性质,vali=Cn1ni=Cn1i1val_i=C_{n-1}^{n-i}=C_{n-1}^{i-1}。证毕!

    于是我们统计总贡献即可。记模数为 pp,注意到计算 CabC_{a}^{b} 时,可能出现 p<ap<a 的情况,不能使用费马小定理求解逆元从而计算组合数。于是我们使用卢卡斯定理求解逆元。(此为前置知识,此处不做详解)

    细节全讲

    此处讲解贪心思想的实现方式。我使用了一个双端队列和一个栈。每次取出最大的两个数,插入队尾和队首,没有数了就停止。容易证明其正确性。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int Mod = 10007;
    int n;
    deque <int> dq;
    stack <int> stk;
    int ans;
    int fac[Mod];
    int fastPow(int base, int power, int mod) { // 快速幂
    	int res = 1;
    	while (power) {
    		if (power & 1)
    			res = res * base % mod;
    		base = base * base % mod;
    		power >>= 1;
    	}
    	return res;
    }
    int inverse(int a, int mod) { // 费马小定理求解逆元
    	return fastPow(fac[a], mod-2, mod);
    }
    int C(int n, int r, int mod) { // 求组合数
    	if (r > n)
    		return 0;
    	int res = (fac[n] * inverse(r, mod) % mod) * inverse(n-r, mod) % mod;
    	return res;
    }
    int Lucas(int n, int r, int mod) { // 卢卡斯定理的应用
    	if (!r)
    		return 1;
    	int res = Lucas(n / mod, r / mod, mod) * C(n % mod, r % mod, mod) % mod;
    	return res;
    }
    int main(void) {
    	cin.tie(0), cout.tie(0);
    	cin >> n;
    	if (!n) {
    		cout << 0;
    		return 0;
    	}
    	fac[0] = 1; // 预处理阶乘
    	for (int i = 1; i < Mod; ++i)
    		fac[i] = fac[i-1] * i % Mod;
    	for (int i = 1; i <= n; ++i) // 大的数在栈顶
    		stk.push(i);
    	while (!stk.empty()) { // 两端进行插入
    		int top = stk.top();
    		stk.pop();
    		dq.push_back(top);
    		if (!stk.empty()) {
    			top = stk.top();
    			stk.pop();
    			dq.push_front(top);
    		}
    	}
    	for (int i = 0; i < n; ++i) { // 统计答案
    		dq.front() %= Mod;
    		ans = (ans + Lucas(n-1, i, Mod) * dq.front() % Mod) % Mod;
    		dq.pop_front();
    	}
    	cout << ans;
    	return 0;
    }
    
    • 1

    信息

    ID
    1713
    时间
    1000ms
    内存
    125MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者