拉题链接 https://vjudge.net/contest/430219#overview
原题链接 https://codeforces.com/problemset/problem/340/C
前言
cf 1600的题, 直接拿来给大一的做, 感觉有亿点点难, 这是个纯数学题, 我用的排列组合方法推导
题目
题意(其实我觉得还是看上边的Note好理解)
给n个数(分别为a1, a2 ...... an-1, 把这n个数全排(共Ann 个序列)一遍, 对于每个序列, 值=每个|ai - ai-1|之和(i = 1~n) , 其中, i = 0时, 为|a1-0|
然后, 将这n的阶乘个式子的值加起来, 先用res表示, 最后输出res/g 和 Ann / g (g为res与Ann 最大公因数)
题解
硬做会超时, 要想着归纳
一 : 当对于一种序列, 比如2 3 5(注意5在最后面): |2 – 0| + |3 – 2| + |5 – 3| = 5; 5只出现1次, 其余2 3都出现2次,
即: 一种序列中最后面的数出现一次, 前n-1个数出现2次
====> 在这所有排列中, 每个数出现总次数 = 2 * (n的阶乘) - (n-1的阶乘)
对式子的解释: 2 * (n的阶乘): 所有数全排的种类数 * 一种排列出现两次; (n - 1的阶乘): 当这个数在最后时, 前面的数全排
二 : 先把绝对值拆开, 比如 |2 – 0| + |3 – 2| + |5 – 3| = 2 + 3 - 2 + 5 - 3 ,最大值5一定是正的, 3与2搭配时-->3为正; 3与5搭配时-->3为负
即: 一个数与比它大的数搭配(挨着,不分前后)时, 它为负, 与比它小的数搭配时, 它为正
====> 设大于n的数有m个, 减去一个数的次数(为负的次数) = 2 * m * (n - 1的阶乘)
对式子的解释: 2为该数与另一个数的两种排列, m: 从m个比它大的数挑一个, 也就是Cm1 ; (n - 1的阶乘) : 该数与另一个数绑定后全排
重点来了, 结果快来了
这个数最后是加减了多少倍呢?
每个数出现的次数
= 正的次数 - 负的次数
= ( 出现的总次数 - 负的次数 ) - 负的次数
= 第一个式子 - 2 * 第二个式子
= [2 * (n的阶乘) - (n-1的阶乘)] - [2 * ( 2 * m * (n - 1的阶乘) )]
那结果 = [ 2 * (n的阶乘) - (n-1的阶乘) - 2 * 2 * m * (n - 1的阶乘) ] 和 n的阶乘 ~~~(约去n-1的阶乘)~~~
= 2 * n - 1 - 4 * m 和 n
m = 大于该数的个数, 对数组小到大排序后(下标从0开始), 大于该数的个数 = n - i - 1, 带入上式即可
(化简 = 2n-1-4n+4i+4 = 4i-2n+3)
代码
#include <iostream> #include <algorithm> using namespace std; typedef long long ll; const int N = 1e5 + 10; ll res = 0, fenmu = 1; ll a[N]; ll gcd(ll a, ll b) { return b? gcd(b, a % b): a; } int main() { int n; cin >> n; for(int i = 0; i < n; i ++) scanf("%lld", &a[i]); sort(a, a+n); for(int i = 0; i < n; i ++) res += (2 * n - 1 - 4 * (n - i - 1)) * a[i]; // res += (4i-2n+3) * a[i]; int g = gcd(res, n); cout << res/g << ' '<< n/g << endl; return 0; }