这场就不说啥了,打的很爆炸。。
漏了个return false结果函数返回值有问题,下次一定要注意。
D 题又是一个无敌容斥原理。。看来这个东西得好好补补了。
下面的题解算是对CF的editorial的理解和大致翻译吧
问你有一个(A,B,C)的平行六面体,让后让你找出(a,b,c),能够用(a,b,c)把它填满。 问这样的(a,b,c)有几种。
首先肯定要找能整除A,B,C的,也就是找因子的个数,用d(A)来表示A的因子数。
下一步就是要用这些因子来组合了,也就是从A取一个,B取一个,C取一个来组合,于是就需要用容斥来考虑重复的情况。
假设原来三条边是(A,B,C),假设我们要找的一种情况是213,表示小六面体的第二条边整除A,第1条边整除B,第三条边整除C。
现在重新假设我们找到了132,231这两种情况,但这两种情况可能会有交集,交集数就是d(gcd(A,B)) * d(c) * d( gcd(A,B) ),所以代码里要用forn (mask, 1 << 6)
来表示所有的组合情况,来进行容斥。
这样容斥完之后,ans1里面包含的,像1x2x3这样三个都不同的被记了六次,(1x2x3, 1x3x2, 2x1x3, 2x3x1, 3x1x2,3x2x1), 1x1x2 被记了三次 (1x1x2, 1x2x1 and 2x1x1) 然后 1x1x1 被记了一次。
所以还要算一个ans2。
ans2是完全假设小六面体第一维第二维相同,也就是a=b,那这个组数就是d( gcd(a,b) ) * d( c ),同样也是同上面的容斥方式,最终 1x1x2 被记了1次, 然后 1x1x1 被记了一次。 所以把ans2 * 3加到ans1里面。
这样1*2*3被记了6次,1*1*2被记了6次,1*1*1被记了4次,所以最后把1*1*1的两次补上,也就是d( gcd(A,B,C) )。
代码来自CF
#include <bits/stdc++.h>
using namespace std;
#define forn(i, n) for (int i = 0; i < (int)(n); ++i)
inline int gcd(int a, int b) {
return a ? gcd(b % a, a) : b;
}
int nd[100100];
vector<int> p[6];
int f(int *a) {
int g[8];
g[1] = nd[a[0]];
g[2] = nd[a[1]];
g[4] = nd[a[2]];
g[3] = nd[gcd(a[0], a[1])];
g[5] = nd[gcd(a[0], a[2])];
g[6] = nd[gcd(a[1], a[2])];
g[7] = nd[gcd(a[0], gcd(a[1], a[2]))];
int ans1 = 0, ans2 = 0;
int x[3];
forn (mask, 1 << 6) {
if (mask == 0) {
continue;
}
x[0] = x[1] = x[2] = 0;
int cnt = 0;
forn (i, 6) {
if (mask & (1 << i)) {
++cnt;
forn (j, 3) {
x[j] |= 1 << p[i][j];
}
}
}
if (cnt & 1) {
ans1 += g[x[0]] * g[x[1]] * g[x[2]];
ans2 += g[x[0] | x[1]] * g[x[2]];
} else {
ans1 -= g[x[0]] * g[x[1]] * g[x[2]];
ans2 -= g[x[0] | x[1]] * g[x[2]];
}
}
return (ans1 + ans2 * 3 + g[7] * 2) / 6;
}
int main() {
int pcnt = 0;
forn (i, 3) {
forn (j, 3) {
if (i == j) {
continue;
}
p[pcnt++] = {i, j, 3 - i - j};
}
}
for (int i = 1; i < 100100; ++i) {
for (int j = 1; j * j <= i; ++j) {
if (i % j) {
continue;
}
++nd[i];
if (j * j < i) {
++nd[i];
}
}
}
int T = 100000;
scanf("%d", &T);
forn (q, T) {
int a[3];
scanf("%d%d%d", &a[0], &a[1], &a[2]);
printf("%d\n", f(a));
}
return 0;
}