[Codeforces] number theory (R1600) Part.1

[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  (3n1e5).第二行输入 n n n个整数 0 0 0 1 1 1.

思路

枚举间隔的点数 d d d,则有解的必要条件是 d ∣ n d\mid n dn且多边形边数 n d ≥ 3 \dfrac{n}{d}\geq 3 dn3.枚举环上的起点,检查正多边形的顶点是否都是 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(cntn).

代码

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  (1a,b1e9).第二行输入一个整数 t    ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t  (1t1e4),表示询问次数.接下来 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  (1lr1e9).

思路

预处理出 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| ps和任意整数 i ∈ [ 1 , ∣ s ∣ p ] i\in\left[1,\dfrac{|s|}{p}\right] i[1,ps],有 s p = s p ⋅ i s_p=s_{p\cdot i} sp=spi,若能,输出"YES"并输出任一方案;否则输出"NO".

思路

显然 s s s中除下标 1 1 1和下标为 > ∣ s ∣ 2 >\dfrac{|s|}{2} >2s的素数外的下标的字符应相同,它们都与下标 2 2 2的字符相同.

[] 对下标 p o s > ∣ s ∣ 2 pos>\dfrac{|s|}{2} pos>2s的位置,

①若 p o s pos pos非素数,则存在一个 ≤ ∣ s ∣ 2 \leq\dfrac{|s|}{2} 2s的素数 p   s . t .   p ∣ p o s p\ s.t.\ p\mid pos p s.t. ppos,进而下标 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} >2s的素数外的下标的字符.

代码

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  (1n1e13),问最后谁胜利.若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 1n 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  (1n,m1e5),分别表示机器数和操作数.接下来 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 1n 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  (3n2e4).第二行输入 n n n个整数 a 1 , ⋯   , a n    ( − 1000 ≤ a i ≤ 1000 ) a_1,\cdots,a_n\ \ (-1000\leq a_i\leq 1000) a1,,an  (1000ai1000).

思路

显然相邻两保留的冰雕间的距离为 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  (1n11184)个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  (1a,n1e7,a+n11e7),问 a , a + 1 , ⋯   , a + n − 1 a,a+1,\cdots,a+n-1 a,a+1,,a+n1 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  (1nk);② 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,n1)+F(k,n2)++F(k,nk)  (n>k),其中 n , k ∈ Z n,k\in\mathbb{Z} n,kZ.给定一个正整数 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  (1s,k1e9,k>1).

第一行输出分解成的 k − k- kbonacci数的个数,第二行输出分解.若有多组解,输出任一组.

思路

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,n1)+F(k,n2)++F(k,nk)  (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,n1)=F(k,n2)+F(k,n3)++F(k,nk1)

​ 两式相减得 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,nk1)=2F(k,n1).

移项得 2 F ( k , n − 1 ) ≥ F ( k , n )    ( n > k ) 2F(k,n-1)\geq F(k,n)\ \ (n>k) 2F(k,n1)F(k,n)  (n>k).显然该不等式当 n ≤ k n\leq k nk时也成立.

贪心,将 s s s减去能减去的最大的 k − k- kbonacci数直至 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- kbonacci数,其中求和的部分可类似于滑动窗口.

代码

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  (1t1e5)组测试数据.每组测试数据输入一个整数 x    ( 1 ≤ x ≤ 1 e 12 ) x\ \ (1\leq x\leq 1\mathrm{e}12) x  (1x1e12).

对每组测试数据,若 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();
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值