codeforces #198 div.2 解题报告

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);
    }
}


 

posted @ 2013-09-06 03:33  leejuen  阅读(170)  评论(0编辑  收藏  举报