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