codeforces #198 div.2 解题报告

来源:互联网 发布:601519历史交易数据 编辑:程序博客网 时间:2024/04/24 12:24

A 题 签到题。不过我挺水的中途被hack成功了。。。后来一直在测数据。

这题用gcd求出lcm。然后根据公式就可以直接计算获得

#include<stdio.h>int Gcd(int a,int b){    if(b>0)    {        a=a%b;        return Gcd(b,a);    }    else return a;}int main(){    int x,y,a,b,i,j,c,k;    while(scanf("%d %d %d %d",&x,&y,&a,&b)!=EOF)    {        c=0;        if(x>y) k=Gcd(x,y);        else k=Gcd(y,x);        k=x*y/k;        if(a%k) printf("%d\n",b/k-a/k);        else printf("%d\n",b/k-a/k+1);    }}

B题,这道题目出题者明显出难了。作者也在事后给了道歉原话:I want to apologize for not estimating the real difficulty of this task. It turns out that it was more complicated than we thought it might be.

我就是我这么机智的弱菜怎么不会。其实这个题目用到的知识我是见过的。但没想到。这里首先你必须知道任意多边形的面积公式。

|x1 y1||x2 y2||x3 y3||x5 y5||x6 y6||x7 y7|   |xn-1 yn-1||xn  yn ||x2 y2|+|x3 y3|+|x4 y4|+|x6 y6|+|x7 y7|+|x8 y8|...+|xn   yn  |+ |x1  y1 |*0.5=S

这里用到的知识点就是你要枚举平面内所有的直线,再根据直线枚举所有的点与这条直线构成的三角形的面积。

网上的一般代码都是这个公式的简化,所以希望读者能过牢记这个万能的计算任意多边形(凹、凸)的公式。

而这个叉积的面积当点为逆时针排列时S为负数,当点为正时针排列时S为正数。也就是说如果从与这条直线所构成的三角形中选两个组成四边形,那么这两个点必须分布在直线两侧,使得他们分别与直线构成的三角形通过公式计算出的值为一正一负。设直线为 ab那么枚举的点c与之组成的序列acb可能是正也可能是负数。我们要用贪婪的方式保留这两个最大子面积。

#include<stdio.h>#include<string.h>#include<math.h>typedef struct Point{int x,y;}Point;Point p[305];double area(Point a,Point b,Point c){return a.x*b.y-b.x*a.y+b.x*c.y-c.x*b.y+c.x*a.y-a.x*c.y;    /*公式直接计算*/}double max(double s1,double s2){return s1>s2?s1:s2;}int main(){int n;while(scanf("%d",&n)!=EOF){int i,j,k;double s1,s2,s3,s;for(i=0;i<n;i++) scanf("%d %d",&p[i].x,&p[i].y);s3=0;for(i=0;i<n;i++){for(j=i+1;j<n;j++){    s1=s2=0;for(k=0;k<n;k++){if(k!=i&&k!=j){s=area(p[i],p[k],p[j]);if(s>0) s1=max(s1,fabs(s)/2);else if(s<0) s2=max(s2,fabs(s)/2);}}if(s1!=0&&s2!=0)    s3=max(s1+s2,s3);    //子面积必须分布在直线两侧,若某个子面积为0.则说明所有点在这条直线的同一侧。见测试#23}}printf("%.6lf\n",s3);}    return 0;}

C 题


Consider 6 possible routes:

  • [2, 3, 5]: total distance traveled: |2 – 0| + |3 – 2| + |5 – 3| = 5;
  • [2, 5, 3]: |2 – 0| + |5 – 2| + |3 – 5| = 7;
  • [3, 2, 5]: |3 – 0| + |2 – 3| + |5 – 2| = 7;
  • [3, 5, 2]: |3 – 0| + |5 – 3| + |2 – 5| = 8;
  • [5, 2, 3]: |5 – 0| + |2 – 5| + |3 – 2| = 9;
  • [5, 3, 2]: |5 – 0| + |3 – 5| + |2 – 3| = 8.

The average travel distance is = =.


这周似乎都是数学题,这道题是组合问题。首先你要推一下公式。

我们看到最终结果中分母是6。这个6就是A(3,3);

而分子是不同元素相减的结果。可以发现除了0以外其他元素都是数组中的。

可以将分子C分解成c1=|2-0|+|2-0|+|3-0|+|3-0|+.....+|5-0|+|5-0|;

c2=|3-2|+|5-3|+.....|2-3|;

可以发现每个组合会出现2次即A(2,2)次;

那么推广一下c1=sum(a[n])*A(n-1,n-1);

c2=C(2,n)*A(n-1,n-1)*sum(abs(a[i]-a[j]));

c2+c1=A(n-1,n-1)*(sum(a[n])+2*sum(a[i]-a[j])); a[i]>a[j]

与分母A(n,n)进行化简得;

结果为(sum(a[n])+2*sum(a[i]-a[j]))/n; a[i]>a[j]

可是要枚举所有的组合是会超时的,但根据先用冒泡排序,再根据c2的组成结构我们也能很好得发现规律。

如例子中c2=2*((3-1)+(3-2)+(2-1));

轻松得发现3出现了2次,2出现1次,-2出现1次,-1出现2次,1出现0次;

这说明当排序好后c2=c2+(i-j)*a[i]; 即a[i]在c2中的贡献是比a[i]小的数字个数减去比a[i]数字大的个数乘a[i];

例如例子中比2大的数只有1个,比2小的数也只有1个那么2的贡献为0;例如数字1;比1大的有两个那么1的贡献为-2*1;

#include<stdio.h>#include<stdlib.h>#include <algorithm>using namespace std;__int64 gcd(__int64 a,__int64 b){    if(b==0) return a;    return gcd(b,a%b);}__int64 a[100005];int main(){    int n;    while(scanf("%d",&n)!=EOF)    {        int i,j;        __int64 c1,c2,cnt,g;        c1=c2=0;        for(i=0;i<n;i++)        {            scanf("%I64d",&a[i]);            c2+=a[i];        }        /*for(i=0;i<n;i++)        {            for(j=i+1;j<n;j++)            {                if(a[i]>a[j]) c1+=a[i]-a[j];                else c1+=a[j]-a[i];            }        }*/        sort(a,a+n);                //        for(i=0,j=n-1;i<n;i++,j--)  //        {                           //            c1+=(i-j)*a[i];         //        }                           //        c1*=2;        cnt=c1+c2;        g=gcd(cnt,n);        printf("%I64d %I64d\n",cnt/g,n/g);    }}


原创粉丝点击