[Codeforces] number theory (R1600) Part.1
题单:https://codeforces.com/problemset/page/1?tags=number+theory%2C1201-1600
71C. Round Table Knights
原题指路:https://codeforces.com/problemset/problem/71/C
题意 ( 0.5 s ) (0.5\ \mathrm{s}) (0.5 s)
n n n个人等间距地围成一圈,好人用 1 1 1表示,坏人用 0 0 0表示.问是否存在一个只以好人为顶点的(非退化)正多边形.
第一行输入一个整数 n ( 3 ≤ n ≤ 1 e 5 ) n\ \ (3\leq n\leq 1\mathrm{e}5) n (3≤n≤1e5).第二行输入 n n n个整数 0 0 0或 1 1 1.
思路
枚举间隔的点数 d d d,则有解的必要条件是 d ∣ n d\mid n d∣n且多边形边数 n d ≥ 3 \dfrac{n}{d}\geq 3 dn≥3.枚举环上的起点,检查正多边形的顶点是否都是 1 1 1.
1 e 5 1\mathrm{e}5 1e5内的整数的约数个数最多为 c n t = 128 cnt=128 cnt=128,对每个约数至多需检查常数遍 n n n个顶点,时间复杂度 O ( c n t ⋅ n ) O(cnt\cdot n) O(cnt⋅n).
代码
void solve() {
int n; cin >> n;
vi a(n);
for (auto& ai : a) cin >> ai;
for (int d = 1; d <= n; d++) { // 枚举间隔
if (n / d < 3) break; // 后面的间隔都太大
if (n % d) continue;
for (int begin = 0; begin < d; begin++) { // 枚举起点
bool ok = true;
for (int i = begin; i < n; i += d) { // 检查多边形顶点
if (!a[i]) {
ok = false;
break;
}
}
if (ok) {
cout << "YES" << endl;
return;
}
}
}
cout << "NO" << endl;
}
int main() {
solve();
}
75C. Modified GCD
原题指路:https://codeforces.com/problemset/problem/75/C
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
给定两正整数 a , b a,b a,b和区间 [ l , r ] [l,r] [l,r],求 a a a与 b b b在区间 [ l , r ] [l,r] [l,r]内的 gcd \gcd gcd,若无解则输出 − 1 -1 −1.
第一行输入两个整数 a , b ( 1 ≤ a , b ≤ 1 e 9 ) a,b\ \ (1\leq a,b\leq 1\mathrm{e}9) a,b (1≤a,b≤1e9).第二行输入一个整数 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4),表示询问次数.接下来 t t t行每行输入两个整数 l , r ( 1 ≤ l ≤ r ≤ 1 e 9 ) l,r\ \ (1\leq l\leq r\leq 1\mathrm{e}9) l,r (1≤l≤r≤1e9).
思路
预处理出 a a a和 b b b各自的因数,两集合求交即公约数,二分是否存在 [ l , r ] [l,r] [l,r]内的约数即可.
代码
vii factors;
void get_factors(int n) {
for (int i = 2, d = 1; (ll)i * i <= n; i += d, d = 2) {
if (n % i == 0) {
factors.push_back({ i,0 });
while (n % i == 0) {
n /= i;
factors.back().second++;
}
}
}
if (n > 1) factors.push_back({ n,1 });
}
vi divisors;
void get_divisors(int idx = 0, int product = 1) {
if (idx == factors.size()) {
divisors.push_back(product);
return;
}
for (int i = 0; i <= factors[idx].second; i++) {
get_divisors(idx + 1, product);
product *= factors[idx].first;
}
}
void solve() {
int a, b; cin >> a >> b;
get_factors(a);
get_divisors();
vi d1 = divisors; // a的约数
factors.clear();
divisors.clear();
get_factors(b);
get_divisors();
vi d2 = divisors; // b的约数
sort(all(d1)), sort(all(d2));
vi common; // a与b的公约数
set_intersection(all(d1), all(d2), inserter(common, common.begin()));
CaseT{
int l, r; cin >> l >> r;
int pos = upper_bound(all(common), r) - common.begin() - 1;
cout << (pos < 0 || common[pos] < l ? -1 : common[pos]) << endl;
}
}
int main() {
solve();
}
123A. Prime Permutation
原题指路:https://codeforces.com/problemset/problem/123/A
题意
给定一个长度为 ∣ s ∣ |s| ∣s∣的、只包含小写英文字母的字符串 s s s,下标从 1 1 1开始.问是否能重排 s s s中字符的顺序使得对任意素数 p ≤ ∣ s ∣ p\leq |s| p≤∣s∣和任意整数 i ∈ [ 1 , ∣ s ∣ p ] i\in\left[1,\dfrac{|s|}{p}\right] i∈[1,p∣s∣],有 s p = s p ⋅ i s_p=s_{p\cdot i} sp=sp⋅i,若能,输出"YES"并输出任一方案;否则输出"NO".
思路
显然 s s s中除下标 1 1 1和下标为 > ∣ s ∣ 2 >\dfrac{|s|}{2} >2∣s∣的素数外的下标的字符应相同,它们都与下标 2 2 2的字符相同.
[证] 对下标 p o s > ∣ s ∣ 2 pos>\dfrac{|s|}{2} pos>2∣s∣的位置,
①若 p o s pos pos非素数,则存在一个 ≤ ∣ s ∣ 2 \leq\dfrac{|s|}{2} ≤2∣s∣的素数 p s . t . p ∣ p o s p\ s.t.\ p\mid pos p s.t. p∣pos,进而下标 p o s pos pos的字符与下标 2 2 2的字符相同.
②若 p o s pos pos是素数,则该位置的字符可任意.
最优策略:将 s s s中出现次数最多的字符作为 s s s中除下标 1 1 1和下标为 > ∣ s ∣ 2 >\dfrac{|s|}{2} >2∣s∣的素数外的下标的字符.
代码
const int MAXN = 1005, MAXM = 30;
int n;
char str[MAXN];
int cnt[MAXM];
bool same[MAXN]; // 记录字符是否应相同
void solve() {
cin >> str + 1;
n = strlen(str + 1);
for (int i = 1; i <= n; i++) cnt[str[i] - 'a']++;
int idx = 0; // 出现次数最多的字符的下标
for (int i = 0; i < 26; i++)
if (cnt[i] > cnt[idx]) idx = i;
// 预处理出需要相同的字符的下标
for (int i = 2; i * i <= n; i++) {
if (!same[i])
for (int j = i * i; j <= n; j += i) same[j] = true;
}
for (int i = 2; 2 * i <= n; i++) same[i] = true;
for (int i = 1; i <= n; i++) { // 需要相同的字符的位置放出现次数最多的字符
if (same[i]) {
if (!cnt[idx]) {
cout << "NO";
return;
}
str[i] = idx + 'a';
cnt[idx]--;
}
}
idx = 0; // 清空
for (int i = 1; i <= n; i++) { // 放剩下的位置
if (!same[i]) {
while (!cnt[idx]) idx++;
str[i] = idx + 'a';
cnt[idx]--;
}
}
cout << "YES" << endl << str + 1;
}
int main() {
solve();
}
150A. Win or Freeze
原题指路:https://codeforces.com/problemset/problem/150/A
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
A和B两人玩游戏,A先手.初始时纸上有一个整数 n n n,每轮每人写出一个上一个写的数的非平凡因子(非 1 1 1和它本身的因子),不能操作者胜.给定 n ( 1 ≤ n ≤ 1 e 13 ) n\ \ (1\leq n\leq 1\mathrm{e}13) n (1≤n≤1e13),问最后谁胜利.若A胜,第一行输出 1 1 1,第二行输出他的第一步操作,若他无法进行第一次操作,输出 0 0 0;若B胜,输出 2 2 2.
思路
只有当 n n n形如 p q pq pq或 p 2 p^2 p2时B胜,其中 p , q p,q p,q为素因子.
对其余情况,先预处理出 n n n的素因子后,若素因子个数为 1 1 1(含重复的素因子),则 n n n为素数,进而A无法进行第一次操作,输出 1 1 1;否则A至少有两个素因子,输出前两个素因子之积即可.
代码
void solve() {
ll n; cin >> n;
if (n == 1) {
cout << 1 << endl << 0;
return;
}
vl divisors;
for (ll i = 2; i <= sqrt(n); i++) {
if (n % i == 0) {
while (n % i == 0) {
n /= i;
divisors.push_back(i);
}
}
}
if(n > 1) divisors.push_back(n);
if (divisors.size() == 2) cout << 2;
else {
cout << 1 << endl;
cout << (divisors.size() == 1 ? 0 : divisors[0] * divisors[1]);
}
}
int main() {
solve();
}
154B. Colliders
原题指路:https://codeforces.com/problemset/problem/154/B
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
有编号 1 ∼ n 1\sim n 1∼n的 n n n个机器,要求正在工作的机器的编号两两互素.
现有两种操作:
(1) + i +\ i + i,表示开启 i i i号机器.
①若成功开启,则输出"Success".
②若 i i i号机器已开启,则输出"Already on".
③若开启 i i i机器后,存在 j j j号机器使得 gcd ( i , j ) ≠ 1 \gcd(i,j)\neq 1 gcd(i,j)=1,则输出"Conflict with j",此时不开启 i i i号机器.
(2) − i -\ i − i,表示关闭 i i i号机器.
①若成功关闭,则输出"Success".
②若 i i i号机器已关闭,则输出"Already off".
第一行输入两个整数 n , m ( 1 ≤ n , m ≤ 1 e 5 ) n,m\ \ (1\leq n,m\leq 1\mathrm{e}5) n,m (1≤n,m≤1e5),分别表示机器数和操作数.接下来 m m m每行输入一个操作,格式如上,数据保证操作合法.
思路
预处理出 [ 1 , n ] [1,n] [1,n]中每个数的素因数后,用set模拟上述过程即可.
代码
const int MAXN = 1e5 + 5;
vi divisors[MAXN]; // divisors[i]表示i的素因子
bool on[MAXN]; // 记录每个机器是否开启
set<int> s[MAXN]; // s[i]记录当前开启的i的倍数的编号
vi get_divisors(int n) {
vi res;
for (int i = 2; (ll)i * i <= n; i++) {
if (n % i == 0) {
res.push_back(i);
while (n % i == 0) n /= i;
}
}
if (n > 1) res.push_back(n);
return res;
}
void solve() {
int n; cin >> n;
for (int i = 1; i <= n; i++) // 预处理出[1,n]中所有数的素因子
divisors[i] = get_divisors(i);
CaseT{
char op; int x; cin >> op >> x;
if (op == '+') {
if (on[x]) {
cout << "Already on" << endl;
continue;
}
int conflict = 0; // 冲突的机器编号,0表示无冲突
for (auto i : divisors[x]) {
if (s[i].size()) {
conflict = *s[i].begin();
break;
}
}
if (conflict) cout << "Conflict with " << conflict << endl;
else {
cout << "Success" << endl;
on[x] = true;
for (auto i : divisors[x]) s[i].insert(x);
}
}
else {
if (!on[x]) cout << "Already off" << endl;
else {
cout << "Success" << endl;
on[x] = false;
for (auto i : divisors[x]) s[i].erase(x);
}
}
}
}
int main() {
solve();
}
158D. Ice Sculptures
原题指路:https://codeforces.com/problemset/problem/158/D
题意 ( 3 s ) (3\ \mathrm{s}) (3 s)
顺时针编号 1 ∼ n 1\sim n 1∼n的 n n n个冰雕等间距地围成一圈,其中 i i i号冰雕的好看程度为 a i a_i ai.现要融化若干个(可能为零个)冰雕,使得剩下的冰雕构成一个(非退化的)正多边形,且好看程度之和最大,求好看程度之和的最大值.
第一行输入一个整数 n ( 3 ≤ n ≤ 2 e 4 ) n\ \ (3\leq n\leq 2\mathrm{e}4) n (3≤n≤2e4).第二行输入 n n n个整数 a 1 , ⋯ , a n ( − 1000 ≤ a i ≤ 1000 ) a_1,\cdots,a_n\ \ (-1000\leq a_i\leq 1000) a1,⋯,an (−1000≤ai≤1000).
思路
显然相邻两保留的冰雕间的距离为 n n n的约数.枚举 n n n的约数 d d d,对每个 d d d,枚举起点 b e g i n begin begin,统计并更新答案即可.
代码
void solve() {
int n; cin >> n;
vi a(n);
for (int& ai : a) cin >> ai;
int ans = -INF;
for (int d = 1; d <= n; d++) { // 枚举间隔
if (n / d < 3) break; // 后面间隔都太大
if (n % d) continue;
for (int begin = 0; begin < d; begin++) { // 枚举起点
int res = 0;
for (int i = begin; i < n; i += d) res += a[i];
ans = max(ans, res);
}
}
cout << ans;
}
int main() {
solve();
}
171F. ucyhf
原题指路:https://codeforces.com/problemset/problem/171/F
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
称一个素数是emirp的,如果它翻转后是与其本身不同的素数.求第 n ( 1 ≤ n ≤ 11184 ) n\ \ (1\leq n\leq 11184) n (1≤n≤11184)个emirp的素数,下标从 1 1 1开始.
思路
暴力枚举即可.
第 11184 11184 11184个emirp的素数是 999983 999983 999983,故答案不会爆int.
代码
int get_rev(int n) {
string s = to_string(n);
reverse(all(s));
return stoi(s);
}
bool is_prime(int n) {
if (n <= 2) return n == 2;
for (int i = 2; (ll)i * i <= n; i++)
if (n % i == 0) return false;
return true;
}
void solve() {
int n; cin >> n;
int ans = 12; // 从第一个emirp13减1开始
while (n) {
ans++;
if (ans != get_rev(ans) && is_prime(ans) && is_prime(get_rev(ans))) n--;
}
cout << ans;
}
int main() {
solve();
}
172D. Calendar Reform
原题指路:https://codeforces.com/problemset/problem/172/D
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
给定正整数 a , n ( 1 ≤ a , n ≤ 1 e 7 , a + n − 1 ≤ 1 e 7 ) a,n\ \ (1\leq a,n\leq 1\mathrm{e}7,a+n-1\leq 1\mathrm{e}7) a,n (1≤a,n≤1e7,a+n−1≤1e7),问 a , a + 1 , ⋯ , a + n − 1 a,a+1,\cdots,a+n-1 a,a+1,⋯,a+n−1这 n n n个数能拆成多少个正的完全平方数之和.
思路
设能整除数 j j j的最大完全平方数为 i 2 i^2 i2.先用筛法预处理出 1 e 7 1\mathrm{e}7 1e7内的 r e s [ j ] = j i 2 res[j]=\dfrac{j}{i^2} res[j]=i2j,再对给定的范围统计答案即可.
代码
const int MAXN = 1e7 + 5;
int res[MAXN];
void init() {
for (int i = 1; (ll)i * i < MAXN; i++) {
for (int j = i * i; j < MAXN; j += i * i)
res[j] = j / (i * i);
}
}
void solve() {
init();
int a, n; cin >> a >> n;
ll ans = 0;
for (int i = a; i <= a + n - 1; i++) ans += res[i];
cout << ans;
}
int main() {
solve();
}
255B. Well-known Numbers
原题指路:https://codeforces.com/problemset/problem/225/B
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
定义 k k k-bonacci数如下:① F ( k , n ) = 0 ( 1 ≤ n ≤ k ) F(k,n)=0\ \ (1\leq n\leq k) F(k,n)=0 (1≤n≤k);② F ( k , k ) = 1 F(k,k)=1 F(k,k)=1;③ F ( k , n ) = F ( k , n − 1 ) + F ( k , n − 2 ) + ⋯ + F ( k , n − k ) ( n > k ) F(k,n)=F(k,n-1)+F(k,n-2)+\cdots+F(k,n-k)\ \ (n>k) F(k,n)=F(k,n−1)+F(k,n−2)+⋯+F(k,n−k) (n>k),其中 n , k ∈ Z n,k\in\mathbb{Z} n,k∈Z.给定一个正整数 s s s,将其分解为若干个(至少两个)相异的 k k k-bonacci数之和.
第一行输入两个整数 s , k ( 1 ≤ s , k ≤ 1 e 9 , k > 1 ) s,k\ \ (1\leq s,k\leq 1\mathrm{e}9,k>1) s,k (1≤s,k≤1e9,k>1).
第一行输出分解成的 k − k- k−bonacci数的个数,第二行输出分解.若有多组解,输出任一组.
思路
F ( k , n ) = F ( k , n − 1 ) + F ( k , n − 2 ) + ⋯ + F ( k , n − k ) ( n > k ) F(k,n)=F(k,n-1)+F(k,n-2)+\cdots+F(k,n-k)\ \ (n>k) F(k,n)=F(k,n−1)+F(k,n−2)+⋯+F(k,n−k) (n>k),
F ( k , n − 1 ) = F ( k , n − 2 ) + F ( k , n − 3 ) + ⋯ + F ( k , n − k − 1 ) F(k,n-1)=F(k,n-2)+F(k,n-3)+\cdots+F(k,n-k-1) F(k,n−1)=F(k,n−2)+F(k,n−3)+⋯+F(k,n−k−1),
两式相减得 F ( k , n ) + F ( k , n − k − 1 ) = 2 F ( k , n − 1 ) F(k,n)+F(k,n-k-1)=2F(k,n-1) F(k,n)+F(k,n−k−1)=2F(k,n−1).
移项得 2 F ( k , n − 1 ) ≥ F ( k , n ) ( n > k ) 2F(k,n-1)\geq F(k,n)\ \ (n>k) 2F(k,n−1)≥F(k,n) (n>k).显然该不等式当 n ≤ k n\leq k n≤k时也成立.
贪心,将 s s s减去能减去的最大的 k − k- k−bonacci数直至 s = 0 s=0 s=0.
[证] 只需证明分解中不会出现两个相等的数.若不然,设分解中存在两相等的数 F ( k , x ) F(k,x) F(k,x).
注意到 2 F ( k , x ) ≥ F ( k , x + 1 ) 2F(k,x)\geq F(k,x+1) 2F(k,x)≥F(k,x+1),则可将两相等的数替换为 F ( k , x + 1 ) F(k,x+1) F(k,x+1).
预处理出不超过 s s s的相异的 k − k- k−bonacci数,其中求和的部分可类似于滑动窗口.
代码
ll s, k;
umap<int, ll> kbon;
vl res; // 存不同的k-bonacci数
ll cal(int n) { // 求kbon[n]
if (n < k) return kbon[n] = 0;
if (n == k) return kbon[n] = 1;
return kbon[n];
}
void init() { // 预处理出不超过s的k-bonacci数
res.push_back(0), res.push_back(1);
ll cur = 1;
int idx = k + 1;
kbon[idx] = cur;
while (cur <= s) {
cur -= cal(idx - k), cur += cal(idx);
kbon[++idx] = cur;
if (res.back() != cur) res.push_back(cur);
}
}
void solve() {
cin >> s >> k;
init();
vi ans;
while (res.size()) {
if (res.back() <= s) {
ans.push_back(res.back());
s -= res.back();
}
res.pop_back();
}
cout << ans.size() << endl;
for (auto i : ans) cout << i << ' ';
}
int main() {
solve();
}
230B. T-primes
原题指路:https://codeforces.com/problemset/problem/230/B
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
称一个正整数是T-prime的,如果它有且只有三个相异的约数.
有 t ( 1 ≤ t ≤ 1 e 5 ) t\ \ (1\leq t\leq 1\mathrm{e}5) t (1≤t≤1e5)组测试数据.每组测试数据输入一个整数 x ( 1 ≤ x ≤ 1 e 12 ) x\ \ (1\leq x\leq 1\mathrm{e}12) x (1≤x≤1e12).
对每组测试数据,若 x x x是T-prime的,输出"YES";否则输出"NO".
思路
显然只有素数的平方是T-prime的.预处理出 1 e 6 1\mathrm{e}6 1e6内的素数,将它们的平方加入set中即可.
代码
const int MAXN = 1e6 + 5;
int primes[MAXN], cnt;
bool state[MAXN];
set<ll> s;
void init() {
for (int i = 2; i < MAXN; i++) {
if(!state[i]) primes[cnt++] = i;
for (int j = 0; (ll)primes[j] * i < MAXN; j++) {
state[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
for (int i = 0; i < cnt; i++) s.insert((ll)primes[i] * primes[i]);
}
void solve() {
init();
CaseT{
ll n; cin >> n;
if(s.count(n)) cout << "YES" << endl;
else cout << "NO" << endl;
}
}
int main() {
solve();
}