【2767】2017年10月10日提高组T1 xjh的旅行

本文介绍了一种计算旅行者访问多个目的地所有可能路径平均距离的方法。通过将问题转化为求所有两点间路径贡献总和,利用排序和递推技巧简化计算过程。

问题描述
lahub是一个旅行者的粉丝,他想成为一个真正的旅行者,所以他计划开始一段旅行。lahub想去参观n个目的地(都在一条直道上)。lahub在起点开始他的旅行。第i个目的地和起点的距离为ai千米(ai为非负整数)。不存在两个目的地和起点的距离相同。

从第i个目的地走到第j个目的地所走的路程为 |ai-aj|千米。我们把参观n个目的地的顺序称作一次“旅行”。lahub可以参观他想要参观的任意顺序,但是每个目的地有且只能被参观一次(参观顺序为n的排列)。

lahub把所有可能的“旅行”都写在一张纸上,并且记下每个“旅行”所要走的路程。他对所有“旅行”的路程之和的平均值感兴趣。但是他觉得计算太枯燥了,所以就向你寻求帮助。
输入
第一行一个正整数n。

第二行n个非负整数a1,a2,….,an(1≤ai≤10^7)。
输出
两个整数,答案用最简分数形式输出,第一个为分子,第二个为分母。
样例输入
3

2 3 5
样例输出
22 3
算法讨论
考虑将这些路径拆成许多的两点间的路径,考虑两点间的路径对答案的贡献。
先考虑起点到Ai这种路径,这条路径确定之后,后面的路径方案就与这条路径无关,后面的方案数一共有 (n-1)!,由于它是单向边且不能改变位置,所以这种路径对路径和的贡献为(n-1)!*Ai。
现在考虑任意两点路径Ai,Aj,除了这两个点的其他的n-2个点方案总数为(n-2)!,而我们可以将这条路径插入在任意的一个位置,共有n-1个位置,故这条路径的方案总数为(n-2)!*(n-1)即(n-1)!,对路径和的贡献为(n-1)!*abs(Ai-Aj)。
所以路径和就转化为求(n-1)!Σabs(Ai-Aj)(1<=i,j<=N,i≠j)+(n-1)!ΣAi。
问题的关键就是求这Σabs(Ai-Aj)。
将所有的Ai排序后,实际上只要考虑从大的点往小的点走,最后路径和*2即可。
然后考虑如何计算:
假设a1 > a2 > a3 > a4,
以Ai为结尾的Ai-Aj的和为Si,
那么:
S2 = a1 - a2
S3 = a1 - a3 + a2 - a3 = (a1 - a2 + a2 - a3) + a2 - a3 = S2 + 2 *(a2 - a3)
S4 = a1 - a4 + a2 - a4 + a3 - a4 = (a1 - a3 + a3 - a4) + (a2 - a3 + a3 - a4) + (a3 - a4)
= S3 + 3 * (a3 - a4)
于是发现Si其实是可以递推的。
答案中分子的(n-1)!与分母的n!相约后只剩一个n,求出c=gcd(ans,n)再分别除以c输出即可。

#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100001
long long a[maxn];
long long n;
long long s,s1,t;

long long gcd(long long a,long long b)
{
    if (b==0)
        return a;
    else
        gcd(b,a%b);
}

long long read()
{
    long long x = 0, p = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') p = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();}
    return x * p;
}
void write(long long ans)
{
    if (ans >= 10)
        write(ans / 10);
    putchar(ans % 10 + '0');
}
int main()
{
    scanf("%lld",&n);
    for (int i=1;i<=n;i++)
    {
        a[i] = read();
        s+=a[i];
    }
    sort(a+1,a+n+1);
    for (int i=1;i<=n;i++)
    {
        s1+=a[i]*(i-1);
        s1-=a[i]*(n-i);
    }
    s+=s1*2;
    t=gcd(s,n);
    write(s/t);
    putchar(' ');
    write(n/t);
}

这里写图片描述
Pixiv ID:56805327

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值