Round #198 (Div. 2)C、Tourist Problem

1、题目链接:http://codeforces.com/problemset/problem/340/C

2、这道题我在比赛的时候一点思路都没有,比赛结束后就去网上搜题解。可能是因为水平有限吧,我感觉都看不太懂,于是干脆自己想了。

3、这题刚开始比较乱,但也容易想到其实本质就是统计“每对结点间距离”出现的次数。我从最简单的只有两个点的情况开始考虑,比如2,3,发现最左边的2有点特殊,因为(0,2)出现了两次,而(2,3)和(3,2)都只出现了一次。再扩展到3个结点,2,3,5,发现(0,2)(0,3),(0,5)依旧比较特殊,像这样的结点对出现的次数是(n-1)!(比如(0,2),到达2后,要选择到3,5两点,可以先到3再到5,也可以先到5再到3。)

4、然后是考虑不是从0出发的结点对,比如(3,5),它可以出现在(2,3,5),也可以出现在(3,5,2),假设有n个结点,我们从中选取两个,还有n-2个——比如2,3,5,7,9这五个点,我们从中选取(3,5),则还剩2,7,9.可以有3!种走的方案,比如我们选取了2,7,9这种方案,又因为3,5可以出现在2左边,2、7中间,7,9中间,9右边四种情况,所以还应该乘以4,抽象地说,就是对于每种组合,都应该乘以(n-2)!再乘以n-1,也就是乘以(n-1)!,又因为3,5和5,3是不一样的,所以还要乘以二。

5、这样公式就是(n种距离*(n-1)!+C(n,2)种组合的距离*(n-1)!*2)/n!),分子分母同除以(n-1)!就是((n种距离+C(n,2)种组合的距离*2)/n),可能这样还不是最简的,用gcd再约分一次吧!

6、但是这样交上去还是超时了,我发现算C(n,2)种组合的距离的时候,我的方法不够聪明,就是暴力枚举端点,其实这样比较慢。我看了下别人的代码,学到一种新方法,就是对于n-1个小段,计算每个小段出现的次数,然后用段的距离*次数,全部相加即可。如何统计出现的次数?其实可以用组合的思想。比如2,3,5,7,9这个例子,我们计算(3,5)出现的次数,那么包含(3,5)的线段的个数就是(3,5)出现的次数。线段的右端可以是5,7,9,线段的左端可以是3,2,这样就有3*2=6种线段包含了(3,5),其他情况依次类推,不用我再举例了吧?

7、修改后,顺利AC,具体见代码。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
LL n,a[100010],sum1,sum2,g;
LL gcd(LL a,LL b){
   return b==0?a:gcd(b,a%b);
}
int main()
{
    while(scanf("%I64d",&n)==1){
        sum1=sum2=0;
        for(int i=0;i<n;i++){
           scanf("%I64d",&a[i]);
           sum1+=a[i];
        }
        sort(a,a+n);
        for(int i=1;i<n;i++){
           sum2+=(a[i]-a[i-1])*i*(n-i);
        }
        sum2*=2;
        g=gcd(sum1+sum2,n);
        printf("%I64d %I64d\n",(sum1+sum2)/g,n/g);
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值