本文涉及知识点
P7281 [COCI 2020/2021 #4] Vepar
题目描述
给定两组正整数 { a , a + 1 , ⋯ , b } \{a,a+1,\cdots,b\} {a,a+1,⋯,b} 和 { c , c + 1 , ⋯ , d } \{c,c+1,\cdots,d\} {c,c+1,⋯,d}。判断 c ⋅ ( c + 1 ) ⋯ d c \cdot (c+1)\cdots d c⋅(c+1)⋯d 能否被 a ⋅ ( a + 1 ) ⋯ b a \cdot (a+1)\cdots b a⋅(a+1)⋯b 整除。
输入格式
第一行输入一个整数 t t t,表示数据组数。
接下来的 t t t 行,输入四个整数 a i , b i , c i , d i a_i,b_i,c_i,d_i ai,bi,ci,di。
输出格式
输出共 t t t 行。对于第 i i i 行,如果 c i ⋅ ( c i + 1 ) ⋯ d i c_i \cdot (c_i+1)\cdots d_i ci⋅(ci+1)⋯di 能够被 a i ⋅ ( a i + 1 ) ⋯ b i a_i \cdot (a_i+1)\cdots b_i ai⋅(ai+1)⋯bi 整除,就输出 DA,否则输出 NE。
输入输出样例 #1
输入 #1
2
9 10 3 6
2 5 7 9
输出 #1
DA
NE
输入输出样例 #2
输入 #2
6
1 2 3 4
1 4 2 3
2 3 1 4
1 3 2 4
19 22 55 57
55 57 19 22
输出 #2
DA
NE
DA
DA
DA
DA
说明/提示
样例 1 解释
按照题意分别相乘得到 9 × 10 = 90 9 \times 10=90 9×10=90 和 3 × 4 × 5 × 6 = 360 3 \times 4 \times 5 \times 6=360 3×4×5×6=360。由于 90 90 90 能被 360 360 360 整除,因此输出 DA。
我们得到 2 × 3 × 4 × 5 = 120 2 \times 3 \times 4 \times 5=120 2×3×4×5=120 和 7 × 8 × 9 = 504 7 \times 8 \times 9=504 7×8×9=504。而 120 120 120 不能被 504 504 504 整除,因此输出 NE。
数据规模与约定
本题不采用捆绑评测,通过对应测试点就可以拿到对应的分数,但有若干个约束。
| 约束编号 | 分值 | 数据范围及约定 |
|---|---|---|
| 1 1 1 | 10 10 10 | a i , b i , c i , d i ≤ 50 a_i,b_i,c_i,d_i \le 50 ai,bi,ci,di≤50 |
| 2 2 2 | 20 20 20 | a i , b i , c i , d i ≤ 1000 a_i,b_i,c_i,d_i \le 1000 ai,bi,ci,di≤1000 |
| 3 3 3 | 10 10 10 | a i = 1 a_i=1 ai=1 |
| 4 4 4 | 30 30 30 | 无 |
对于 100 % 100\% 100% 的数据, 1 ≤ t ≤ 10 1 \le t \le 10 1≤t≤10, 1 ≤ a i ≤ b i ≤ 10 7 1 \le a_i \le b_i \le 10^7 1≤ai≤bi≤107, 1 ≤ c i ≤ d i ≤ 10 7 1 \le c_i \le d_i \le 10^7 1≤ci≤di≤107。
说明
本题分值按 COCI 原题设置,满分 70 70 70。
题目译自 COCI2020-2021 CONTEST #4 T2 Vepar。
线性筛 调和级数
M =1e7
所有用例初始化一次:
pri记录[1,M]间的质因数,x = pri.size(),大约1e6。
向量v[i]包括{i1,i2}表示i1的阶乘共有i2个质因数i。
tmp[j]记录包括质因数i的数量。
for( i :pri ){
for(int j =1; ij <= M ;j++){
v[i].emplace_back(j i,tmp[j]+1)
tmp[i*j] = tmp[j]+1;
}
根据v[i] 清理tmp。注意 :如果全清,时间复杂度是O(n)
}
对于每个查询,枚举每个质因数:
通过二分查找[a,b]中此质因数的数量x1,[c,d]中的质因数数量x2。如果x2 > x1,返回false。
如果没有返回false,返回true。
初始化时间复杂度 :O(xlogM)
查询时间复杂度 :O(xlogM)
初始化的时候时间会超限,给向量分配空间太花时间了。
n的阶乘中因子p的数量:
∑ k : 1 p ⌊ n / k p ⌋ \sum_{k:1}^p \lfloor n/k^p \rfloor ∑k:1p⌊n/kp⌋
代码
核心代码
cpp
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>
#include <bitset>
using namespace std;
template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {
in >> pr.first >> pr.second;
return in;
}
template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {
in >> get<0>(t) >> get<1>(t) >> get<2>(t);
return in;
}
template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {
in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);
return in;
}
template<class T = int>
vector<T> Read() {
int n;
cin >> n;
vector<T> ret(n);
for (int i = 0; i < n; i++) {
cin >> ret[i];
}
return ret;
}
template<class T = int>
vector<T> ReadNotNum() {
vector<T> ret;
T tmp;
while (cin >> tmp) {
ret.emplace_back(tmp);
if ('\n' == cin.get()) { break; }
}
return ret;
}
template<class T = int>
vector<T> Read(int n) {
vector<T> ret(n);
for (int i = 0; i < n; i++) {
cin >> ret[i];
}
return ret;
}
class CCreatePrime {
public:
CCreatePrime(int iMax) :m_isPrime(iMax + 1, true)
{
m_isPrime[0] = m_isPrime[1] = false;
for (int i = 2; i <= iMax; i++)
{
if (m_isPrime[i])
{
m_vPrime.emplace_back(i);
}
for (const auto& n : m_vPrime)
{
if ((long long)n * i > iMax) { break; }
m_isPrime[n * i] = false;
if (0 == i % n) { break; }
}
}
}
vector<int> m_vPrime;
vector<bool> m_isPrime;
};
class Solution {
public:
vector<bool> Ans(vector<tuple<int, int, int, int>>& abcd) {
Init();
vector<bool> ans;
for (const auto& [a, b, c, d] : abcd) {
bool bCan = true;
for (const auto& p : s_prime) {
auto Cal = [&](int n) {
int cnt = 0;
for (long long k = p; k <= n; k *= p) {
cnt += n / k;
}
return cnt;
};
if (Cal(d) - Cal(c - 1) < Cal(b) - Cal(a - 1)) { bCan = false; break; }
}
ans.emplace_back(bCan);
}
return ans;
}
void Init() {
if (s_prime.size()) { return; }
const int M = 10'000'000;
s_prime = CCreatePrime(M).m_vPrime;
}
static vector<int> s_prime;
};
vector<int> Solution::s_prime;
int main() {
#ifdef _DEBUG
freopen("a.in", "r", stdin);
#endif // DEBUG
ios::sync_with_stdio(0); cin.tie(nullptr);
auto abcd = Read<tuple<int, int, int, int>>();
#ifdef _DEBUG
//printf("N=%d",n);
//Out(abcd, "abcd=");
//Out(strs2, ",strs2=");
//Out(que, ",que=");
/*Out(que, "que=");*/
#endif // DEBUG
auto res = Solution().Ans(abcd);
for (const auto& b : res)
{
cout << (b ? "DA" : "NE") << "\n";
}
return 0;
}
单元测试
cpp
vector<tuple<int, int, int, int>> abcd;
TEST_METHOD(TestMethod11)
{
abcd = { {9,10,3,6},{2,5,7,9} };
auto res = Solution().Ans(abcd);
AssertV({ true,false}, res);
}
TEST_METHOD(TestMethod12)
{
abcd = { {1,2,3,4},{1,4,2,3},{2,3,1,4},{1,3,2,4},{19,22,55,57},{55,57,19,22} };
auto res = Solution().Ans(abcd);
AssertV({ true,false,true,true,true,true }, res);
}
扩展阅读
| 我想对大家说的话 |
|---|
| 工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
| 学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
| 有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
| 闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
| 子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
| 如果程序是一条龙,那算法就是他的是睛 |
| 失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。