Codeforces CodeTON Round 3 D. Count GCD【状压、容斥原理计数】

2024-04-06 01:12

本文主要是介绍Codeforces CodeTON Round 3 D. Count GCD【状压、容斥原理计数】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

D. Count GCD

D

题意

给定一个长度为 n n n 的正整数数组 a a a ∀ 1 ≤ i ≤ n , a i ≤ m \forall 1 \leq i \leq n,a_i \leq m ∀1inaim

先要要统计满足以下条件的数组 b b b 的数量:

  • ∀ 1 ≤ i ≤ n , b i ≤ m \forall 1 \leq i \leq n,b_i \leq m ∀1inbim
  • g c d ( b 1 , b 2 , b 3 , . . . b i ) = a i gcd(b_1,b_2,b_3,...b_i) = a_i gcd(b1,b2,b3,...bi)=ai
思路

可以发现在当前的 i i i g c d ( a 1 , a 2 , . . . a i − 1 ) = a i − 1 gcd(a_1,a_2,...a_{i -1}) = a_{i -1} gcd(a1,a2,...ai1)=ai1,那么只要 g c d ( a i − 1 , b i ) = a i gcd(a_{i - 1},b_i) = a_i gcd(ai1,bi)=ai 就可以了,那么我们可以得出结论: a i ∣ a i − 1 a_i \mid a_{i - 1} aiai1,也即是 a i − 1 a_{i - 1} ai1 a i a_i ai 的因子,同时 a i ∣ b i a_i \mid b_i aibi

这样子答案就是每个位置可选的数量累乘,问题是如何快速求出位置 i i i 可行的 b i b_i bi 数量

b i b_i bi 一定得是 a i a_i ai 的倍数,我们可以假设 k ⋅ a i = b i k \cdot a_i = b_i kai=bi,那么不同的 k k k 的数量就是 b i b_i bi 的数量
那么 g c d ( a i − 1 , b i ) = a i ⇒ g c d ( a i − 1 a i , k ) = 1 gcd(a_{i - 1}, b_i) = a_i \Rarr gcd(\dfrac{a_{i - 1}}{a_i}, k) = 1 gcd(ai1,bi)=aigcd(aiai1,k)=1,也就是 a i − 1 a i \dfrac{a_{i - 1}}{a_i} aiai1 中出现的质因子不能在 k k k 中出现

为了快速得到 a i − 1 a i \dfrac{a_{i - 1}}{a_i} aiai1 的所有质因子,我们发现 a i − 1 a i \dfrac{a_{i - 1}}{a_i} aiai1 的质因子一定是 a 1 a_1 a1 的质因子。这是因为 a i ∣ a i − 1 ∣ . . . . ∣ a 2 ∣ a 1 a_i \mid a_{i - 1} \mid .... \mid a_2 \mid a_1 aiai1....a2a1;并且最小的 10 10 10 个质数相乘就已经大于 1 0 9 10^9 109 了!所以我们只需要最多 9 9 9 个不同的质数(不一定是最小的 9 9 9 个)就可以表示小于等于 1 0 9 10^9 109 的数了

我们只需要在 O ( m ) O(m) O(m) 下预处理 得出 a 1 a_1 a1质因子,然后对于每个 i > 2 i > 2 i>2,枚举 a 1 a_1 a1 的质因子,看看 a i a_i ai 是否被其整除,即可快速得出 a i a_i ai 的所有质因子,质因子数量都不超过 9 9 9 个!我们这里就可以考虑状压 + 容斥原理来计数了

容斥原理
集合 S S S 的子集 A 1 A_1 A1 具有性质 P 1 P_1 P1,子集 A 2 A_2 A2 具有性质 P 2 P_2 P2,… A n A_n An 具有性质 P n P_n Pn


(1)集合 S S S不具有性质 P 1 , P 2 , . . . , P n P_1,P_2,...,P_n P1,P2,...,Pn 的对象个数为:
∣ A 1 ‾ ∩ A 2 ‾ , . . . , ∩ A n ‾ ∣ = ∣ S ∣ − ∑ ∣ A i ∣ + ∑ ∣ A i ∩ A j ∣ − ∑ ∣ A i ∩ A j ∩ A k ∣ + . . . + ( − 1 ) n ∣ A 1 ∩ A 2 ∩ . . . ∩ A n ∣ | \overline{A_1} \cap \overline{A_2}, ..., \cap \overline{A_n}| = |S| - \sum |A_i| + \sum |A_i \cap A_j| - \sum |A_i \cap A_j \cap A_k| + ... + (-1)^{n} |A_1 \cap A_2 \cap ... \cap A_n| A1A2,...,An=SAi+AiAjAiAjAk+...+(1)nA1A2...An


(2)集合 S S S至少具有性质 P 1 , P 2 , . . . P n P_1,P_2,...P_n P1,P2,...Pn 之一的对象个数为:
∣ A 1 ∪ A 2 , . . . , ∪ A n ∣ = ∑ ∣ A i ∣ − ∑ ∣ A i ∩ A j ∣ + ∑ ∣ A i ∩ A j ∩ A k ∣ + . . . + ( − 1 ) n + 1 ∣ A 1 ∩ A 2 ∩ . . . ∩ A n ∣ | A_1 \cup A_2, ..., \cup A_n| = \sum |A_i| - \sum |A_i \cap A_j| + \sum |A_i \cap A_j \cap A_k| + ... + (-1)^{n + 1} |A_1 \cap A_2 \cap ... \cap A_n| A1A2,...,An=AiAiAj+AiAjAk+...+(1)n+1A1A2...An

因为性质很少,最多只有 9 9 9 个(分别被不同的 9 9 9 个质数整除),我们要求出与 a i − 1 a i \dfrac{a_{i - 1}}{a_i} aiai1 互质的 k k k 的数量,也就是 k k k 不能拥有 a i − 1 a i \dfrac{a_{i - 1}}{a_i} aiai1 的质因子,也就是 k k k 不具有性质 P 1 , P 2 , . . . P k P_1,P_2,...P_k P1,P2,...Pk k k k a i − 1 a i \dfrac{a_{i - 1}}{a_i} aiai1 质因子数),只需要用容斥原理简单地通过整除得到即可,注意 k ≤ m a i k \leq \dfrac{m}{a_i} kaim

时间复杂度: O ( m + 9 ⋅ 2 9 ⋅ log ⁡ m ) O(\sqrt m + 9 \cdot 2^9 \cdot \log m) O(m +929logm)

最后那个 log ⁡ m \log m logm 是因为: a 1 a_1 a1 的每个质因子数量一定非递减,如果某个质因子在 a i − 1 a i \dfrac{a_{i - 1}}{a_i} aiai1 中出现,说明这个质因子数量在这里至少 − 1 -1 1 了,那么从 a 1 a_1 a1 减少到 a n a_n an,质因子最多就 log ⁡ m \log m logm 个,很快就没了

#include<bits/stdc++.h>
#define fore(i,l,r)	for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;const int INF=0x3f3f3f3f;
const long long INFLL=1e18;typedef long long ll;template<class T>
constexpr T power(T a, ll b){T res = 1;while(b){if(b&1) res = res * a;a = a * a;b >>= 1;}return res;
}constexpr ll mul(ll a,ll b,ll mod){ //快速乘,避免两个long long相乘取模溢出ll res = a * b - ll(1.L * a * b / mod) * mod;res %= mod;if(res < 0) res += mod; //误差return res;
}template<ll P>
struct MLL{ll x;constexpr MLL() = default;constexpr MLL(ll x) : x(norm(x % getMod())) {}static ll Mod;constexpr static ll getMod(){if(P > 0) return P;return Mod;}constexpr static void setMod(int _Mod){Mod = _Mod;}constexpr ll norm(ll x) const{if(x < 0){x += getMod();}if(x >= getMod()){x -= getMod();}return x;}constexpr ll val() const{return x;}explicit constexpr operator ll() const{ return x; //将结构体显示转换为ll类型: ll res = static_cast<ll>(OBJ)}constexpr MLL operator -() const{ //负号,等价于加上ModMLL res;res.x = norm(getMod() - x);return res;}constexpr MLL inv() const{assert(x != 0);return power(*this, getMod() - 2); //用费马小定理求逆}constexpr MLL& operator *= (MLL rhs) & { //& 表示“this”指针不能指向一个临时对象或const对象x = mul(x, rhs.x, getMod()); //该函数只能被一个左值调用return *this;}constexpr MLL& operator += (MLL rhs) & {x = norm(x + rhs.x);return *this;}constexpr MLL& operator -= (MLL rhs) & {x = norm(x - rhs.x);return *this;}constexpr MLL& operator /= (MLL rhs) & {return *this *= rhs.inv();}friend constexpr MLL operator * (MLL lhs, MLL rhs){MLL res = lhs;res *= rhs;return res;}friend constexpr MLL operator + (MLL lhs, MLL rhs){MLL res = lhs;res += rhs;return res;}friend constexpr MLL operator - (MLL lhs, MLL rhs){MLL res = lhs;res -= rhs;return res;}friend constexpr MLL operator / (MLL lhs, MLL rhs){MLL res = lhs;res /= rhs;return res;}friend constexpr std::istream& operator >> (std::istream& is, MLL& a){ll v;is >> v;a = MLL(v);return is;}friend constexpr std::ostream& operator << (std::ostream& os, MLL& a){return os << a.val();}friend constexpr bool operator == (MLL lhs, MLL rhs){return lhs.val() == rhs.val();}friend constexpr bool operator != (MLL lhs, MLL rhs){return lhs.val() != rhs.val();}
};const ll mod = 998244353;
using Z = MLL<mod>;std::vector<int> get_d(int x){std::vector<int> d;for(int i = 2; i * i <= x; ++i){if(x % i ) continue;d.push_back(i);while(x % i == 0) x /= i;}if(x > 1) d.push_back(x);return d;
}void solve(){int n, m;std::cin >> n >> m;std::vector<int> a(n + 1);fore(i, 1, n + 1) std::cin >> a[i];fore(i, 2, n + 1)if(a[i - 1] % a[i]){std::cout << "0\n";return;}auto div = get_d(a[1]);Z ans = 1;fore(i, 2, n + 1){int val = a[i - 1] / a[i];std::vector<int> left;for(auto d : div)if(val % d == 0)left.push_back(d);int sz = left.size();ll K = m / a[i]; //k的上界ll tot = K;fore(mask, 1, 1 << sz){int cnt = __builtin_popcount(mask); //容斥原理系数int now = 1;fore(j, 0, sz)if(mask >> j & 1)now *= left[j];if(cnt & 1) tot -= K / now;else tot += K / now;}ans *= tot;}std::cout << ans << endl;
}int main(){std::ios::sync_with_stdio(false);std::cin.tie(nullptr);std::cout.tie(nullptr);int t;std::cin >> t;while(t--){solve();}return 0;
}

这篇关于Codeforces CodeTON Round 3 D. Count GCD【状压、容斥原理计数】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/878638

相关文章

MySQL中的MVCC底层原理解读

《MySQL中的MVCC底层原理解读》本文详细介绍了MySQL中的多版本并发控制(MVCC)机制,包括版本链、ReadView以及在不同事务隔离级别下MVCC的工作原理,通过一个具体的示例演示了在可重... 目录简介ReadView版本链演示过程总结简介MVCC(Multi-Version Concurr

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Redis主从复制的原理分析

《Redis主从复制的原理分析》Redis主从复制通过将数据镜像到多个从节点,实现高可用性和扩展性,主从复制包括初次全量同步和增量同步两个阶段,为优化复制性能,可以采用AOF持久化、调整复制超时时间、... 目录Redis主从复制的原理主从复制概述配置主从复制数据同步过程复制一致性与延迟故障转移机制监控与维

SpringCloud配置动态更新原理解析

《SpringCloud配置动态更新原理解析》在微服务架构的浩瀚星海中,服务配置的动态更新如同魔法一般,能够让应用在不重启的情况下,实时响应配置的变更,SpringCloud作为微服务架构中的佼佼者,... 目录一、SpringBoot、Cloud配置的读取二、SpringCloud配置动态刷新三、更新@R

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

hdu4407(容斥原理)

题意:给一串数字1,2,......n,两个操作:1、修改第k个数字,2、查询区间[l,r]中与n互质的数之和。 解题思路:咱一看,像线段树,但是如果用线段树做,那么每个区间一定要记录所有的素因子,这样会超内存。然后我就做不来了。后来看了题解,原来是用容斥原理来做的。还记得这道题目吗?求区间[1,r]中与p互质的数的个数,如果不会的话就先去做那题吧。现在这题是求区间[l,r]中与n互质的数的和

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯:

Codeforces Round #261 (Div. 2)小记

A  XX注意最后输出满足条件,我也不知道为什么写的这么长。 #define X first#define Y secondvector<pair<int , int> > a ;int can(pair<int , int> c){return -1000 <= c.X && c.X <= 1000&& -1000 <= c.Y && c.Y <= 1000 ;}int m

Codeforces Beta Round #47 C凸包 (最终写法)

题意慢慢看。 typedef long long LL ;int cmp(double x){if(fabs(x) < 1e-8) return 0 ;return x > 0 ? 1 : -1 ;}struct point{double x , y ;point(){}point(double _x , double _y):x(_x) , y(_y){}point op