Experimental Educational Round: VolBIT Formulas Blitz
题目链接:https://codeforces.com/contest/630
A - Again Twenty Five!
?
C - Lucky Numbers
?
B - Moore's Law
?
D - Hexagons!
?
J - Divisibility
?
注意[2,10]的lcm是 \(2^3*3^2*5*7\) ,也就是每种质因子取最高次。为2520。
F - Selection of Personnel
?
这个组合数 \(C_{777}^7\) 刚好不会溢出ll,一种防溢出的组合数的写法,先处理出分母这个比较小的,然后对于分子的每个部分都先和分母约分再乘上去,这样保证中间的结果都不会超过答案的 \(C_{777}^7\) 。
ll C(int n, int m) {
ll up = 1, down = 1;
for(int i = 1; i <= m; ++i)
down *= i;
for(int i = 1; i <= m; ++i) {
ll tmp = (n + 1 - i);
ll g = __gcd(tmp, down);
tmp /= g;
down /= g;
up *= tmp;
}
return up / down;
}
R - Game
题意:有一个 \(n*n\) 的空白棋盘,两个玩家轮流涂黑格子,每次可以选一个与已经涂黑的格子没有公共边的格子图。无法操作的人输。求谁赢。
题解:这种题目有一个“模仿对手”的策略,易知若 \(n\) 为偶数,那么每次先手选择之后,后手选择对应的中心对称的位置,必然是可以的,所以最后一定会是先手输;若 \(n\) 为奇数,一开始先手先把正中间占了,然后又变成了可以跟着后手选中心对称的位置,最后一定是后手输。
N - Forecast
题意:求一个二次方程的两个不同的实根,保证有两个不同的实根,先输出大的,再输出小的。
题解:由于a的符号可能是负的,所以未必一定有 \(x_1=\frac{-b+\sqrt{b^2-4ac}}{2a}>x_2=\frac{-b-\sqrt{b^2-4ac}}{2a}\) ,需要判断然后交换。
K - Indivisibility
题意:求[1,n]内有多少个数,不被[2,10]其中任意一个数整除。
题解:易知不被[2,3,5,7]整除就是不被[2,10]的任意一个数整除,那么就是容斥原理,乘个莫比乌斯函数就可以了,注意不要漏了。
void TestCase() {
ll n;
scanf("%lld", &n);
ll ans = n;
ans -= n / 2;
ans -= n / 3;
ans -= n / 5;
ans -= n / 7;
ans += n / 6;
ans += n / 10;
ans += n / 14;
ans += n / 15;
ans += n / 21;
ans += n / 35;
ans -= n / 30;
ans -= n / 42;
ans -= n / 70;
ans -= n / 105;
ans += n / 210;
printf("%lld\n", ans);
}
那么这个式子看起来像什么呢?反正不是正常的莫比乌斯函数求和,求解的复杂度应该就是 2^k ,k是参与容斥的质数的个数。
H - Benches
题意:在一个 \(n*n\) 的棋盘上面放5个相同的棋子,要求同一行和同一列至多有一个棋子,问有多少种放法。
题解:第一次放,有 \(n*n\) 种放法,然后去掉这一行一列,第二次放有 \((n-1)*(n-1)\) 种放法……注意最后棋子是无序的,把这个全排列除掉。
小心溢出,经过严密的计算,最终结果应该就是1e18的级别,是不会溢出的,也要像上面那样分次约分。
void TestCase() {
ll n;
scanf("%lld", &n);
ll m = 120;
ll res1 = n * n * (n - 1) * (n - 1) * (n - 2) * (n - 2);
ll g = __gcd(res1, m);
res1 /= g;
m /= g;
ll res2 = (n - 3) * (n - 3) * (n - 4) * (n - 4);
g = __gcd(res2, m);
res2 /= g;
m /= g;
printf("%lld\n", res1 * res2 / m);
}
L - Cracking the Code
?
要注意的是,需要输出前导零。
G - Challenge Pennants
题意:有n个不同的盒子,5个相同的红球,3个相同的蓝球,每个球都要放在一个盒子里,一个盒子可以放任意个球。求多少种方法。
题解:首先把红蓝球分步,然后把5个红球的分法分类:
5、4+1、3+2、3+1+1、2+2+1、2+1+1+1、1+1+1+1+1
这些用组合数很好算。
ll C(ll n, ll m) {
if(n < m)
return 0;
ll up = 1, down = 1;
for(int i = 1; i <= m; ++i) {
up *= (n + 1 - i);
down *= i;
}
return up / down;
}
void TestCase() {
int n;
scanf("%d", &n);
ll res1 = 0;
res1 += C(n, 1);
res1 += C(n, 1) * C(n - 1, 1);
res1 += C(n, 1) * C(n - 1, 1);
res1 += C(n, 1) * C(n - 1, 2);
res1 += C(n, 2) * C(n - 2, 1);
res1 += C(n, 1) * C(n - 1, 3);
res1 += C(n, 5);
ll res2 = 0;
res2 += C(n, 1);
res2 += C(n, 1) * C(n - 1, 1);
res2 += C(n, 3);
printf("%lld\n", res1 * res2);
}
I - Parking Lot
和昨晚(2020/3/23)的Edu一样。
E - A rectangle
题意:给出一个矩形的左下角和右上角的坐标,求把这个矩形涂黑需要涂黑多少个六边形?
按这个图理解,应该是分奇偶性讨论一下。但是WA22了,是这个图有误导性。
void TestCase() {
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
if((x1 - x2) % 2 == 0) {
//有奇数列
if((x1 - y1) % 2 == 0) {
//左下角在中心
if((x2 - y2) % 2 == 0) {
//右上角也在中心
ll dy = abs(y2 - y1) / 2;
//少的列是dy
ll dx = abs(x2 - x1) + 1;
//有奇数列
ll sum = (dx - 1) / 2 * dy + (dx + 1) / 2 * (dy + 1);
printf("%lld\n", sum);
return;
} else {
//右上角在边界
ll dy = abs(y2 - y1) / 2 + 1;
//每列都是dy
ll dx = abs(x2 - x1) + 1;
//有奇数列
ll sum = dx * dy;
printf("%lld\n", sum);
return;
}
} else {
//左下角在边界
if((x2 - y2) % 2 == 0) {
//右上角在中心
ll dy = abs(y2 - y1) / 2 + 1;
//每列都是dy
ll dx = abs(x2 - x1) + 1;
//有奇数列
ll sum = dx * dy;
printf("%lld\n", sum);
return;
} else {
//右上角也在边界
ll dy = abs(y2 - y1) / 2;
//少的列是dy
ll dx = abs(x2 - x1) + 1;
//有奇数列
ll sum = (dx - 1) / 2 * (dy + 1) + (dx + 1) / 2 * dy;
printf("%lld\n", sum);
return;
}
}
} else {
//有偶数列
if((x1 - y1) % 2 == 0) {
//左下角在中心
if((x2 - y2) % 2 == 0) {
//右上角也在中心
ll dy = abs(y2 - y1) / 2 + 1;
//每列都是dy
ll dx = abs(x2 - x1) + 1;
//有偶数列
ll sum = dx * dy;
printf("%lld\n", sum);
return;
} else {
//右上角在边界
ll dy = abs(y2 - y1) / 2;
//少的列是dy
ll dx = abs(x2 - x1) + 1;
//有偶数列
ll sum = dx / 2 * dy + dx / 2 * (dy + 1);
printf("%lld\n", sum);
return;
}
} else {
//左下角在边界
if((x2 - y2) % 2 == 0) {
//右上角在中心
ll dy = abs(y2 - y1) / 2;
//少的列是dy
ll dx = abs(x2 - x1) + 1;
//有偶数列
ll sum = dx / 2 * dy + dx / 2 * (dy + 1);
printf("%lld\n", sum);
return;
} else {
//右上角也在边界
ll dy = abs(y2 - y1) / 2 + 1;
//每列都是dy
ll dx = abs(x2 - x1) + 1;
//有偶数列
ll sum = dx * dy;
printf("%lld\n", sum);
return;
}
}
}
}
原来CF也有错题的,不过有人喷过了。这道题还限制了输入只有奇数列的情况。对于输入1 0 5 6
,按照上图来理解应该是17个(3+4+3+4+3),但是答案却是18个(4+3+4+3+4),我表示强烈谴责。
void TestCase() {
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
ll dy = abs(y2 - y1) / 2;
ll dx = abs(x2 - x1) + 1;
ll sum = (dx - 1) / 2 * dy + (dx + 1) / 2 * (dy + 1);
printf("%lld\n", sum);
}
M - Turn
题意:要求将一张照片顺时针旋转d度。分两个步骤:顺时针旋转x次90度;顺时针或逆时针旋转nd度。要求在nd最小的前提下,最小化x。
题解:易知直接把d模360到[0,359],然后枚举旋转0次、1次、2次、3次的度数(0,90,180,270)算出与d的夹角的值,找到最小的那个输出。注意是夹角,所以并非是两者作差这么简单,比如要把0度理解为0度或者360度。或者说,算出两个[0,359]的角的差的绝对值abs之后,还要取min(abs,360-abs)才是夹角。
void TestCase() {
ll d;
scanf("%lld", &d);
d %= 360;
if(d < 0)
d += 360;
int ans = INF, cnt = 0;
for(int i = 0; i < 4; ++i) {
int nd = abs(d - 90 * i);
if(min(nd, 360 - nd) < ans) {
ans = min(nd, 360 - nd);
cnt = i;
}
}
printf("%d\n", cnt);
}
Q - Pyramids
题意:给出所有边都相等的正三棱锥、正四棱锥、正五棱锥的棱长,求体积。
题解:由底面的正n边形的棱长可以算出正n边形对应的外接圆的半径。方法是:从底面圆心向各个顶点连线,划分成若干个全等的等腰三角形,可以轻松计算出等腰三角形的底角的大小(通过顶角是圆心角,或者内角和公式 \(\pi(n-2)\) 都可以算出来),然后用棱长的一半,除以这个底角的cos值,就得到了底面外接圆的半径。得到了底面外接圆的半径,联系棱长,就可以求出这个棱锥的高(勾股定理)。最后要求底面正多边形的面积,这个可以直接计算出圆心角,然后用三角形面积公式 \(\frac{1}{2}\sin \alpha r^2\) 算出每个等腰三角形的面积。最后体积要记得除以3。也就是 \(V_{锥}=\frac{1}{3}S_{底}h\) 。
const double PI = acos(-1.0);
double calc_r(double l, int n) {
double s = PI * (n - 2);
double d = (s / n) / 2.0;
return (l / 2.0) / cos(d);
}
double calc_s(double r, int n) {
double s = 0.5 * sin(2.0 * PI / n) * r * r;
return s * n;
}
void TestCase() {
double l3, l4, l5;
scanf("%lf%lf%lf", &l3, &l4, &l5);
double r3 = calc_r(l3, 3);
double h3 = sqrt(l3 * l3 - r3 * r3);
double s3 = calc_s(r3, 3);
double r4 = calc_r(l4, 4);
double h4 = sqrt(l4 * l4 - r4 * r4);
double s4 = calc_s(r4, 4);
double r5 = calc_r(l5, 5);
double h5 = sqrt(l5 * l5 - r5 * r5);
double s5 = calc_s(r5, 5);
printf("%.12f\n", (s3 * h3 + s4 * h4 + s5 * h5) / 3.0);
}