Codeforces Round #497 (Div. 2) D - Pave the Parallelepiped

这场就不说啥了,打的很爆炸。。
漏了个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;
}

猜你喜欢

转载自blog.csdn.net/z631681297/article/details/81055713