CF 1008D Pave the Parallelepiped

2023-05-16

          • 传送门
          • 题目大意
            • 样例输入
            • 样例输出
            • 样例解释
          • 思路
          • 参考代码
          • 总结

传送门
题目大意

给你一个 A×B×C A × B × C 的长方体,你要把它切成很多块大小都是 a×b×c a × b × c 的小长方体,要求只能沿 x x 轴等距切,沿 y 轴等距切,沿 z z 轴等距切。问所有切的方案中能够切出多少种不同的小长方体。规定 abc,也就是说 1×2×1 1 × 2 × 1 的小长方体和 1×1×2 1 × 1 × 2 的小长方体算一种小长方体。

t t 组询问。t105 1A,B,C105 1 ≤ A , B , C ≤ 10 5

样例输入
4
1 1 1
1 6 1
2 2 2
100 100 100
样例输出
1
4
4
165
样例解释

对于第二组询问,有以下四种方案: 1×1×1 1 × 1 × 1 1×1×2 1 × 1 × 2 1×1×3 1 × 1 × 3 1×1×6 1 × 1 × 6

思路

首先用 O(nn) O ( n n ) 的时间复杂度预处理出所有数的因数。由于 105 10 5 以内的数的因数个数最多只有 128 128 个,因此不用担心空间不够。

一个很 Naive 的想法是,直接把三个数的因数个数乘起来,再来考虑算重的问题。看上去直接乘起来算重的情况是这样的:三个数都不同的情况算了 6 6 次;有两个数相同的情况算了 3 次;三个数都相同的情况算了 1 1 次。发现三个数都相同的情况的方案数很好算,就是三个数都有的因数个数。我们继续考虑如何计算有两个数相同的情况。我们把这三个数的所有因数拿出来。对于只出现了两次的因数,第三个数显然可以取没有出现过的那个数的所有因数;对于出现了三次的因数,我们令前两个因数相同,那么第三个因数可以在所有因数中任取(除了前两个因数)。我们可以在 O(σ0) 的时间复杂度内计算出答案。

但是这样并不对,因为当三个数不同时,算重的情况是这样的:(举个例子,)对于一个三元组 (x,y,z)(xyz) ( x , y , z ) ( x ≤ y ≤ z ) ,它能够被 (A,B,C) ( A , B , C ) 切出来(换句话说, xA x ∣ A yB y ∣ B zC z ∣ C ),也能被 (A,C,B) ( A , C , B ) 切出来,但是不能被 (B,A,C) ( B , A , C ) 切出来。这种情况对于不同的三元组被算重的次数也是不同的,并不是固定的 6 6 次,因此很难下手。

我们假设 (x,y,z) 无需满足 xyz x ≤ y ≤ z ,然后再去统计满足 xA x ∣ A yB y ∣ B zC z ∣ C 的答案。那么对于一个合法的 (a,b,c)(a<b<c) ( a , b , c ) ( a < b < c ) ,它就真的被算了 6 6 次了,其它情况同理。

由于现在 x y y z 相互独立了,那么我们要求的东西实际上是:

(x,y,z)(A,B,C)  (x,y,z)(A,C,B)    (x,y,z)(C,B,A)(xyz) ( x , y , z ) ∣ ( A , B , C )  或  ( x , y , z ) ∣ ( A , C , B )  或  ⋯  或  ( x , y , z ) ∣ ( C , B , A ) ( x ≤ y ≤ z )

考虑容斥,那么我们现在首先要解决的问题是对于一个 (A,B,C) ( A , B , C ) 如何计算 (x,y,z) ( x , y , z ) 的个数。答案显然是它们 σ0 σ 0 的乘积。

考虑如何计算 (A,B,C)(A,C,B) ( A , B , C ) ∧ ( A , C , B ) 的方案数。这相当于是要求 (A,gcd(B,C),gcd(B,C)) ( A , gcd ( B , C ) , gcd ( B , C ) ) 的方案数,还是这么做就可以了。

现在我们得到了 (x,y,z) ( x , y , z ) 不受顺序限制的方案数。对于三者都不同的情况,我们计算了 6 6 次;对于有两者相同的情况,我们计算了 3 次;对于三者相同的情况,我们计算了 1 1 <script type="math/tex" id="MathJax-Element-50">1</script> 次。前面已经说了计算两者相同和相同的计算方法,就这么做完了。

参考代码

注意快速排序是过不了的,必须写归并排序。

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
using LL = long long;
using ULL = unsigned long long;
using std::cin;
using std::cout;
using std::endl;
using INT_PUT = int;
INT_PUT readIn()
{
    INT_PUT a = 0; bool positive = true;
    char ch = getchar();
    while (!(ch == '-' || std::isdigit(ch))) ch = getchar();
    if (ch == '-') { positive = false; ch = getchar(); }
    while (std::isdigit(ch)) { a = a * 10 - (ch - '0'); ch = getchar(); }
    return positive ? -a : a;
}
void printOut(INT_PUT x)
{
    char buffer[20]; int length = 0;
    if (x < 0) putchar('-'); else x = -x;
    do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
    do putchar(buffer[--length]); while (length);
    putchar('\n');
}

const int maxn = int(1e5);
std::vector<std::vector<int>> vec(maxn + 1);
void init()
{
    vec[1].push_back(1);
    for (int i = 2; i <= maxn; i++)
    {
        auto& v = vec[i];
        for (int j = 1; j * j <= i; j++)
        {
            if (!(i % j))
            {
                v.push_back(j);
                v.push_back(i / j);
            }
        }
        std::sort(v.begin(), v.end());
        v.resize(std::unique(v.begin(), v.end()) - v.begin());
    }
}
int Gcd(int a, int b)
{
    return !b ? a : Gcd(b, a % b);
}
int a[6][3];

int ans;
struct Node
{
    int val;
    int type;
    Node() {}
    Node(int val, int type) : val(val), type(type) {}
    bool operator<(const Node& b) const
    {
        if (val != b.val) return val < b.val;
        return type < b.type;
    }
    bool operator==(const Node& b) const
    {
        return val == b.val;
    }
};
std::vector<Node> all;
int CountAll()
{
    auto t = all;
    return std::unique(t.begin(), t.end()) - t.begin();
}

struct Triple
{
    int a, b, c;
    bool operator<(const Triple& y) const
    {
        if (a != y.a) return a < y.a;
        if (b != y.b) return b < y.b;
        return c < y.c;
    }
    Triple() {}
    Triple(int a, int b, int c) : a(a), b(b), c(c) {}
};
std::map<Triple, int> map;

void run()
{
    init();

    int T = readIn();
    while (T--)
    {
        for (int i = 0; i < 3; i++)
            a[0][i] = readIn();
        std::sort(a[0], a[0] + 3);
        auto triple = Triple(a[0][0], a[0][1], a[0][2]);
        if (map.count(triple))
        {
            printOut(map[triple]);
            continue;
        }
        int idx[3] = { 0, 1, 2 };
        for (int i = 1; i < 6; i++)
        {
            std::next_permutation(idx, idx + 3);
            for (int j = 0; j < 3; j++)
                a[i][j] = a[0][idx[j]];
        }

        int ans = 0;
        int U = 1 << 6;
        for (int S = 1; S < U; S++)
        {
            int gcd[3]{};
            int sig = -1;
            for (int i = 0; i < 6; i++)
                if (S & (1 << i))
                {
                    sig = -sig;
                    for (int j = 0; j < 3; j++)
                        gcd[j] = Gcd(a[i][j], gcd[j]);
                }
            ans += vec[gcd[0]].size() * vec[gcd[1]].size() * vec[gcd[2]].size() * sig;
        }

        const auto& vx = vec[a[0][0]];
        const auto& vy = vec[a[0][1]];
        const auto& vz = vec[a[0][2]];
        all.clear();
        int i = 0, j = 0, k = 0;
        while (i < vx.size() || j < vy.size() || k < vz.size())
            if ((j >= vy.size() || (i < vx.size() && vx[i] <= vy[j])) &&
                (k >= vz.size() || (i < vx.size() && vx[i] <= vz[k])))
                all.push_back(Node(vx[i++], 0));
            else if (k >= vz.size() || (j < vy.size() && vy[j] <= vz[k]))
                all.push_back(Node(vy[j++], 1));
            else
                all.push_back(Node(vz[k++], 2));

        int countAll = CountAll();
        int same3 = 0;
        int pre2 = -1, pre1 = -1;
        for (const auto& t : all)
        {
            if (pre1 == pre2 && t.val == pre1)
                same3++;
            pre2 = pre1;
            pre1 = t.val;
        }

        int count_ = 1;
        int same2 = 0;
        for (i = 1; i < all.size(); i++)
        {
            if (all[i].val == all[i - 1].val)
                count_++;
            if (all[i].val != all[i - 1].val || i == all.size() - 1)
            {
                if (count_ == 2)
                {
                    int appear[3]{};
                    if (all[i].val != all[i - 1].val)
                        for (int j = 1; j <= 2; j++)
                            appear[all[i - j].type]++;
                    else
                        for (int j = 0; j < 2; j++)
                            appear[all[i - j].type]++;

                    if (!appear[0])
                        same2 += vx.size();
                    else if (!appear[1])
                        same2 += vy.size();
                    else if (!appear[2])
                        same2 += vz.size();
                }
                else if (count_ == 3)
                    same2 += countAll - 1;
                count_ = 1;
            }
        }

        printOut(map[triple] = (ans + same2 * 3 + same3 * 5) / 6);
    }
}

int main()
{
    run();
    return 0;
}
总结

这道题大体思路是对的:把所有情况算出来,减去算重的。但是一开始的那个思路算重的次数是不定的。突破点是减弱限制,把算重的次数确定,但是代价是要用容斥原理。所以这道题容斥的方法也很重要。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CF 1008D Pave the Parallelepiped 的相关文章

随机推荐

  • Luogu 2375 [NOI 2014] 动物园

    文章目录 传送门思路参考代码Review 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 连 KMP 也不会 xff0c WA 飞了 xff0c 唉 xff0c 我太弱啦 xff01 首先 xff0c 原始的 K
  • Luogu 2114 [NOI 2014] 起床困难综合症

    传送门思路参考代码 传送门 思路 按位贪心 但是我太弱了 xff0c 明明可以 O n O n 预处理 xff0c 我却只会 O 32 n O
  • Luogu 2354 [NOI 2014] 随机数生成器

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 这么一个傻逼题 xff0c 我却看成了要你构造一种交换方案使得答案最小 xff0c 结果后面的额外交换是题目给定的 唉 xff0c 我太弱啦 x
  • Luogu 2305 [NOI 2014] 购票

    传送门思路别人家的题解弱化的传送门 xff08 Luogu 3994 高速公路 xff09 参考代码 对于没有距离限制的 50 分 参考代码 对于 100 分的数据参考代码Remarks 传送门 思路 唉 xff0c 我太弱了 xff0c
  • Luogu 1224 [NOI 2013] 向量内积

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 听都没有听说过这类题 xff0c 唉 xff0c 我太弱啦 xff01 显然这道题可以在 O n 2 d O n 2
  • Luogu 1397 [NOI 2013] 矩阵游戏

    传送门思路正解参考代码Remarks 传送门 思路 唉 xff0c 我太弱了 xff0c T1 都做不来 xff0c 唉 xff0c 我太弱啦 xff01 显然这个题可以用矩阵快速幂在 O log n O log n
  • Luogu 2414 [NOI 2011] 阿狸的打字机

    文章目录 传送门思路参考代码总结 传送门 思路 首先我们甚至不能单独保存每个字符串 xff0c 因为总长度可以达到 O n 2
  • kali新手入门教学(10)--ping的讲解

    Ping 是 Windows 和 Linux 都自带的一个扫描工具 xff0c 用于校验与远程计算机或本机的连接 只有在安装 TCP IP 协议之后才能使用该命令 Ping 命令通过向计算机发送 ICMP 回应 报文并且监听回应报文的返回
  • Luogu 3628 [APIO 2010] 特别行动队

    传送门思路参考代码 传送门 BZOJ 思路 设 f i f i 表示将 1 i 1 i 的士兵编
  • Luogu 1399 [NOI 2013] 快餐店

    传送门思路参考代码Remarks总结 传送门 思路 发现这是一棵环套树 那首先我们会想到树上的情况 如果这是一棵树 xff0c 我们自然会联想到树的直径 xff0c 自然会想到对于树而言 xff0c 答案为直径长度的一半 证明 用反证法 假
  • GDB for C++ in Linux

    这篇文章主要讲讲如何在 Linux 下使用 GDB xff0c 当然 xff0c 就指令而言在 Windows 下也适用 环境Items 编译启动退出加载文件查看源代码断点 插入断点删除断点 运行程序查看变量控制程序执行 继续下一步单步进入
  • CF 1000F One Occurrence

    传送门题目大意思路参考代码总结 时光如梭 xff0c Codeforces 的题号还是三位数的时代已经过去 xff1b 量子语言原来已经来临 传送门 题目大意 给定一个长度为 n n 的序列 a a xff0c 有 m m 个
  • CF 993E Nikita and Order Statistics

    传送门题目大意思路参考代码 传送门 题目大意 给你一个数组 a 1 n a 1 n xff0c 对于 k 61 0 n
  • CF 997A Convert to Ones

    传送门题目大意思路参考代码总结 传送门 题目大意 给你一个长度为 n n 3 10 5 n n 3 10
  • CF 997B Roman Digits

    传送门题目大意思路参考代码总结 传送门 题目大意 现在规定罗马数字只有 4 4 个字符 I V X L 分别代表 1 1 5 5 10 10 50 50
  • CF 997C Sky Full of Stars

    传送门题目大意思路参考代码总结 传送门 题目大意 有一个 n n n 10 6 n n n 10
  • CF 997D Cycles in product

    传送门题目大意思路参考代码总结Remarks参考代码 传送门 题目大意 给你大小分别为 n 1 n 1 和 n 2 n 2
  • NOI 2018 游记

    Day 2Day 1Day 0Day 1Day 2Day 3Day 4Day inftyDay 5 Day 2 昨天打完了最后一个一场模拟赛 xff0c 又垫底啦 xff01 本来之前我很少垫底的 xff0c 不知道为什么最后四场模拟赛一直
  • MySQL集群架构

    第1节 集群架构设计 在集群架构设计时 xff0c 主要遵从下面三个维度 xff1a 可用性 扩展性 一致性 1 1 可用性设计 站点高可用 xff0c 冗余站点 xff1b 服务高可用 xff0c 冗余服务 xff1b 数据高可用 xff
  • CF 1008D Pave the Parallelepiped

    传送门题目大意 样例输入样例输出样例解释 思路参考代码总结 传送门 题目大意 给你一个 A B C A B C 的长方体 xff0c 你要把它切成很多块大小都是 a b c