[Codeforces] number theory (R1600) Part.6
题单:https://codeforces.com/problemset/page/1?tags=number+theory%2C1201-1600
922C. Cave Painting
原题指路:https://codeforces.com/problemset/problem/922/C
题意
给定两整数 n , k ( 1 ≤ n , k ≤ 1 e 18 ) n,k\ \ (1\leq n,k\leq 1\mathrm{e}18) n,k (1≤n,k≤1e18),对所有 i ∈ [ 1 , k ] i\in [1,k] i∈[1,k],问 n m o d i n\ \mathrm{mod}\ i n mod i是否都相异,如是则输出"Yes";否则输出"No".
思路
若余数都相异,则余数为 [ 0 , k − 1 ] [0,k-1] [0,k−1].
①余数 ( k − 1 ) (k-1) (k−1)只能通过模 k k k得到.
②余数 ( k − 2 ) (k-2) (k−2)能通过模 ( k − 1 ) (k-1) (k−1)或 k k k得到,但模 k k k的余数已确定为 ( k − 1 ) (k-1) (k−1),
故余数 ( k − 2 ) (k-2) (k−2)只能通过模 ( k − 1 ) (k-1) (k−1)得到.
显然若有解,则 n m o d i = i − 1 ( 1 ≤ i ≤ k ) n\ \mathrm{mod}\ i=i-1\ \ (1\leq i\leq k) n mod i=i−1 (1≤i≤k),这等价于 i ∣ ( n + 1 ) ( 1 ≤ i ≤ k ) i\mid (n+1)\ \ (1\leq i\leq k) i∣(n+1) (1≤i≤k),则 ( n + 1 ) ≥ l c m ( 1 , ⋯ , k ) (n+1)\geq \mathrm{lcm}(1,\cdots,k) (n+1)≥lcm(1,⋯,k).
因 l c m \mathrm{lcm} lcm增长快,打表知 k ≥ 43 k\geq 43 k≥43时无解.对 k < 43 k<43 k<43的情况,暴力求即可.
代码
void solve() {
ll n, k; cin >> n >> k;
if (k >= 43) cout << "No";
else {
uset<int> s;
for (int i = 1; i <= k; i++) s.insert(n % i);
cout << (s.size() == k ? "Yes" : "No");
}
}
int main() {
solve();
}
937B. Vile Grasshoppers
原题指路:https://codeforces.com/problemset/problem/937/B
题意
给定一素数 p p p和一整数 y ( 2 ≤ p ≤ y ≤ 1 e 9 ) y\ \ (2\leq p\leq y\leq 1\mathrm{e}9) y (2≤p≤y≤1e9),求 ( p , y ] (p,y] (p,y]范围内的最大素数,无解输出 − 1 -1 −1.
思路
若有解 a n s ans ans,则 i ∤ a n s ( 2 ≤ i ≤ p ) i\not\mid ans\ \ (2\leq i\leq p) i∣ans (2≤i≤p).
注意到 1 e 9 1\mathrm{e}9 1e9内的素数间隔不超过 300 300 300,暴力枚举并检验其是否有 ≤ p \leq p ≤p的素因子即可.
代码
int p, y;
bool check(int n) { // 判断n是否含有<=p的素因子
if (n <= 2) return n == 2;
for (int i = 2; (ll)i * i <= n && i <= p; i++)
if (n % i == 0) return false;
return true;
}
void solve() {
cin >> p >> y;
for (int i = y; i > p; i--) {
if (check(i)) {
cout << i;
return;
}
}
cout << -1;
}
int main() {
solve();
}
986A. Fair
原题指路:https://codeforces.com/problemset/problem/986/A
题意 ( 2 s 2\ \mathrm{s} 2 s)
给定一个包含编号 1 ∼ n 1\sim n 1∼n的 n n n个节点和 m m m条边的无向连通图,每条边的权值都为 1 1 1.有 k k k种物品,每个节点处产出一种物品.为举办一次宴会,需至少集齐 s s s种不同的物品.从节点 u u u到节点 v v v需花费 d ( u , v ) d(u,v) d(u,v)的金币,其中 d ( u , v ) d(u,v) d(u,v)是节点 u u u到节点 v v v的最短路.求在每个节点处都举办宴会的最小花费.
第一行输入四个整数 n , m , k , s ( 1 ≤ n ≤ 1 e 5 , 0 ≤ m ≤ 1 e 5 , 1 ≤ s ≤ k ≤ min { n , 100 } ) n,m,k,s\ \ (1\leq n\leq 1\mathrm{e}5,0\leq m\leq 1\mathrm{e}5,1\leq s\leq k\leq \min\{n,100\}) n,m,k,s (1≤n≤1e5,0≤m≤1e5,1≤s≤k≤min{n,100}).第二行输入 n n n个整数 a 1 , ⋯ , a n ( 1 ≤ a i ≤ k ) a_1,\cdots,a_n\ \ (1\leq a_i\leq k) a1,⋯,an (1≤ai≤k),分别表示每个节点产出的物品.数据保证物品 1 ∼ k 1\sim k 1∼k都至少出现一次.接下来 m m m行每行输入两个整数 u , v ( 1 ≤ u , v ≤ n , u ≤ v ) u,v\ \ (1\leq u,v\leq n,u\leq v) u,v (1≤u,v≤n,u≤v),表示节点 u u u与 v v v间存在边.数据保证图连通.
思路
o b j e c t s [ i ] objects[i] objects[i]表示产出物品 i i i的节点编号.对每个物品 i ( 1 ≤ i ≤ k ) i\ \ (1\leq i\leq k) i (1≤i≤k),跑一遍BFS求出从每个城市出发获得该物品的最小代价,则可在 O ( k ( n + m ) ) O(k(n+m)) O(k(n+m))的时间复杂度内求出从每个城市出发获得每个物品的最小代价.
对每个节点,用排序或用nth_element选代价小的 s s s个物品,求代价之和即可.
代码
const int MAXN = 1e5 + 5, MAXM = 105;
int n, m; // 节点数、边数
int k, s; // 物品数、举办宴会所需的物品数
vi objects[MAXM]; // 产出每种物品的节点编号
vi edges[MAXN];
int dis[MAXN]; // dis[u]表示从节点u出发获得当前物品的最小代价
int res[MAXN][MAXM]; // res[u][i]表示从节点u出发获得物品i的最小代价
void solve() {
cin >> n >> m >> k >> s;
for (int i = 1; i <= n; i++) {
int a; cin >> a;
objects[a].push_back(i);
}
while (m--) {
int u, v; cin >> u >> v;
edges[u].push_back(v), edges[v].push_back(u);
}
for (int i = 1; i <= k; i++) { // 枚举物品
for (int j = 1; j <= n; j++) dis[j] = INF; // 初始化
qi que;
for (auto u : objects[i]) {
que.push(u);
dis[u] = 0; // 初始化
}
while (que.size()) {
auto u = que.front(); que.pop();
for (auto v : edges[u]) {
if (dis[v] > dis[u] + 1) {
dis[v] = dis[u] + 1;
que.push(v);
}
}
}
for (int j = 1; j <= n; j++) res[j][i] = dis[j];
}
for (int i = 1; i <= n; i++) {
nth_element(res[i] + 1, res[i] + s + 1, res[i] + k + 1); // 注意物品下标从1开始
int ans = 0;
for (int j = 1; j <= s; j++) ans += res[i][j];
cout << ans << ' ';
}
}
int main() {
solve();
}
992B. Nastya Studies Informatics
原题指路:https://codeforces.com/problemset/problem/992/B
题意
给定四个整数 l , r , x , y ( 1 ≤ l ≤ r ≤ 1 e 9 , 1 ≤ x ≤ y ≤ 1 e 9 ) l,r,x,y\ \ (1\leq l\leq r\leq 1\mathrm{e}9,1\leq x\leq y\leq 1\mathrm{e}9) l,r,x,y (1≤l≤r≤1e9,1≤x≤y≤1e9),求满足 l ≤ a , b ≤ r l\leq a,b\leq r l≤a,b≤r且 gcd ( a , b ) = x , l c m ( a , b ) = y \gcd(a,b)=x,\mathrm{lcm}(a,b)=y gcd(a,b)=x,lcm(a,b)=y的数对 ( a , b ) (a,b) (a,b)的对数.
思路
因 gcd ( a , b ) = x \gcd(a,b)=x gcd(a,b)=x,设 a = c x , b = d x a=cx,b=dx a=cx,b=dx,则 gcd ( c , d ) = 1 \gcd(c,d)=1 gcd(c,d)=1.
因 l ≤ a , b ≤ r l\leq a,b\leq r l≤a,b≤r,则 l x ≤ c , d ≤ r x \dfrac{l}{x}\leq c,d\leq \dfrac{r}{x} xl≤c,d≤xr,即 l ≤ c x , d x ≤ r l\leq cx,dx\leq r l≤cx,dx≤r.
注意到 x y = a b = c d x 2 xy=ab=cdx^2 xy=ab=cdx2,则 y x = c d \dfrac{y}{x}=cd xy=cd.
① x ∤ y x\not\mid y x∣y时, a n s = 0 ans=0 ans=0.
② x ∣ y x\mid y x∣y时,因 y x ≤ 1 e 9 \dfrac{y}{x}\leq 1\mathrm{e}9 xy≤1e9,可枚举 y x \dfrac{y}{x} xy的一对约数 ( c , d ) s . t . c d = y x (c,d)\ s.t.\ cd=\dfrac{y}{x} (c,d) s.t. cd=xy,检查是否满足 l ≤ c x , d x ≤ r l\leq cx,dx\leq r l≤cx,dx≤r且 gcd ( c , d ) = 1 \gcd(c,d)=1 gcd(c,d)=1即可.注意 c = d c=d c=d时对答案的贡献为 1 1 1,否则贡献为 2 2 2.
代码
void solve() {
int l, r, x, y; cin >> l >> r >> x >> y;
if (y % x) {
cout << 0;
return;
}
int n = y / x;
int ans = 0;
for (int d = 1; (ll)d * d <= n; d++) {
if (n % d == 0) {
int c = n / d;
if (l <= c * x && c * x <= r && l <= d * x && d * x <= r && gcd(c, d) == 1)
ans += d * d == n ? 1 : 2;
}
}
cout << ans;
}
int main() {
solve();
}
1005D. Polycarp and Div 3
原题指路:https://codeforces.com/problemset/problem/1005/D
题意 ( 3 s ) (3\ \mathrm{s}) (3 s)
给定一个大整数,用字符串 s s s表示.现要将 s s s划分为若干段,使得每一段表示的数无前导零.求每一段代表的数中是 3 3 3的倍数的数的个数的最大值.
第一行输入一个长度不超过 2 e 5 2\mathrm{e}5 2e5且只包含数字的字符串 s s s,数据保证其代表的整数无前导零.
思路
d p [ i ] dp[i] dp[i]表示将前缀 s [ 1 ⋯ i ] s[1\cdots i] s[1⋯i]划分为若干段后,每一段表示的数中是 3 3 3的倍数的数的个数的最大值.初始条件 d p [ 0 ] = 0 dp[0]=0 dp[0]=0.
以最后一个数码 s [ i ] s[i] s[i]是否属于前面一段分类:
①若 s [ i ] s[i] s[i]不属于前面一段,则 d p [ i ] = d p [ i − 1 ] dp[i]=dp[i-1] dp[i]=dp[i−1].
②若 s [ i ] s[i] s[i]属于前面一段,则需找到最短的$s[j\cdots i]\ s.t.\ $它表示的数是 3 3 3的倍数.
注意到若 s [ j ⋯ i ] s[j\cdots i] s[j⋯i]表示的数是 3 3 3的倍数,则前缀 s [ 1 ⋯ j − 1 ] s[1\cdots j-1] s[1⋯j−1]和 s [ 1 ⋯ i ] s[1\cdots i] s[1⋯i]表示的数模 3 3 3余数相同.
l e n [ r ] len[r] len[r]表示当前已处理完的前缀中,表示的数模 3 3 3余 r r r的最大长度.
l e n [ r ] = − 1 len[r]=-1 len[r]=−1表示不存在相应的前缀.初始条件 l e n [ 0 ] = 0 , l e n [ r ] = − 1 ( r ≠ 0 ) len[0]=0,len[r]=-1\ \ (r\neq 0) len[0]=0,len[r]=−1 (r=0).
设 s [ 1... i ] s[1...i] s[1...i]表示的数模 3 3 3余 r r r,则 l e n [ r ] len[r] len[r]即最大的 j s . t . s [ j ⋯ i ] j\ s.t.\ s[j\cdots i] j s.t. s[j⋯i]表示的数是 3 3 3的倍数.
状态转移方程: d p [ i ] = max { d p [ i ] , d p [ l e n [ r ] ] + 1 } ( l e n ≠ − 1 ) dp[i]=\max\{dp[i],dp[len[r]]+1\}\ \ (len\neq -1) dp[i]=max{dp[i],dp[len[r]]+1} (len=−1),总时间复杂度 O ( n ) O(n) O(n).
代码
const int MAXN = 2e5 + 5;
int n; // s的长度
char s[MAXN];
int dp[MAXN]; // dp[i]表示将前缀s[1...i]划分为若干段后,每一段表示的数中是3的倍数的数的个数的最大值
int len[3] = { 0,-1,-1 }; // len[r]表示当前已处理完的前缀中,表示的数模3余r的最大长度
void solve() {
cin >> s + 1;
n = strlen(s + 1);
int rest = 0; // 前缀模3的余数
for (int i = 1; i <= n; i++) {
rest = (rest + s[i] - '0') % 3;
dp[i] = dp[i - 1];
if (~len[rest]) dp[i] = max(dp[i], dp[len[rest]] + 1);
len[rest] = i;
}
cout << dp[n];
}
int main() {
solve();
}
1025B. Weakened Common Divisor
原题指路:https://codeforces.com/problemset/problem/1025/B
题意 ( 1.5 s 1.5\ \mathrm{s} 1.5 s)
对 n n n个数对 ( a 1 , b 1 ) , ⋯ , ( a n , b n ) (a_1,b_1),\cdots,(a_n,b_n) (a1,b1),⋯,(an,bn),定义它们的Weakened Common Divisor(WCD)为任意 > 1 >1 >1的整数 x x x,使得 x x x至少整除每个数对中的一个数.
给定 n ( 1 ≤ n ≤ 1.5 e 5 ) n\ \ (1\leq n\leq 1.5\mathrm{e}5) n (1≤n≤1.5e5)个数对 ( a 1 , b 1 ) , ⋯ , ( a n , b n ) ( 2 ≤ a i , b i ≤ 2 e 9 ) (a_1,b_1),\cdots,(a_n,b_n)\ \ (2\leq a_i,b_i\leq 2\mathrm{e}9) (a1,b1),⋯,(an,bn) (2≤ai,bi≤2e9),求它们的WCD,若无解则输出 − 1 -1 −1.
思路
显然若一个数是WCD,则其素因子也是WCD,故只需考虑素数.
对 a 1 , b 1 a_1,b_1 a1,b1素因数分解,将素因子存在set中.枚举素因子,检查每个数对中是否至少存在一个该素因子的倍数.设 A = max 1 ≤ i ≤ n { a i , b i } \displaystyle A=\max_{1\leq i\leq n}\{a_i,b_i\} A=1≤i≤nmax{ai,bi},总时间复杂度 O ( A + n log A ) O\left(\sqrt{A}+n\log A\right) O(A+nlogA).
代码
void get(int n, uset<int>& s) { // 将n素因数分解,素因子存在s中
for (int d = 2; (ll)d * d <= n; d++) {
if (n % d == 0) {
s.insert(d);
while (n % d == 0) n /= d;
}
}
if (n > 1) s.insert(n);
}
void solve() {
int n; cin >> n;
vii a(n);
for (auto& [u, v] : a) cin >> u >> v;
uset<int> factors; // 素因子
get(a[0].first, factors), get(a[0].second, factors);
for (auto p : factors) {
bool ok = true;
for (int i = 1; i < n; i++) {
if (a[i].first % p && a[i].second % p) {
ok = false;
break;
}
}
if (ok) {
cout << p;
return;
}
}
cout << -1;
}
int main() {
solve();
}
1056B. Divide Candies
原题指路:https://codeforces.com/problemset/problem/1056/B
题意
给定两整数 n , m ( 1 ≤ n ≤ 1 e 9 , 1 ≤ m ≤ 1000 ) n,m\ \ (1\leq n\leq 1\mathrm{e}9,1\leq m\leq 1000) n,m (1≤n≤1e9,1≤m≤1000),求 ∑ i = 1 n ∑ j = 1 n [ m ∣ ( i 2 + j 2 ) ] \displaystyle\sum_{i=1}^n \sum_{j=1}^n [m\mid (i^2+j^2)] i=1∑nj=1∑n[m∣(i2+j2)].
思路
注意到 ( i 2 + j 2 ) m o d m = [ ( i m o d m ) 2 + ( j m o d m ) 2 ] m o d m (i^2+j^2)\ \mathrm{mod}\ m=[(i\ \mathrm{mod}\ m)^2+(j\ \mathrm{mod}\ m)^2]\ \mathrm{mod}\ m (i2+j2) mod m=[(i mod m)2+(j mod m)2] mod m,则只需检查所有的 i 2 + j 2 ( 0 ≤ i , j < m ) i^2+j^2\ \ (0\leq i,j<m) i2+j2 (0≤i,j<m),乘上周期即可.
代码
int n, m;
int cal(int a) {
return n / m + (a && a <= n % m ? 1 : 0);
}
void solve() {
cin >> n >> m;
ll ans = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < m; j++) {
if ((i * i + j * j) % m == 0)
ans += (ll)cal(i) * cal(j);
}
}
cout << ans;
}
int main() {
solve();
}
1062B. Math
原题指路:https://codeforces.com/problemset/problem/1062/B
题意
给定一个正整数 n ( 1 ≤ n ≤ 1 e 6 ) n\ \ (1\leq n\leq 1\mathrm{e}6) n (1≤n≤1e6).现有如下两种操作:① m u l x mul\ x mul x,表示令 n ∗ = x n*=x n∗=x,其中 x ∈ Z + x\in\mathbb{Z}^+ x∈Z+;② s q r t x sqrt\ x sqrt x,表示令 n = n n=\sqrt{n} n=n,其中 n \sqrt{n} n需为整数.问若干次操作后 n n n的最小值和达到最小值的最小操作次数.
思路
设 n = ∏ i = 1 m p i α i \displaystyle n=\prod_{i=1}^m p_i^{\alpha_i} n=i=1∏mpiαi,显然 n n n的最小值 a n s = ∏ i = 1 m p i \displaystyle ans=\prod_{i=1}^m p_i ans=i=1∏mpi.
因每次操作都将素因子的指数减半,则先进行若干次乘法使得所有素因子的次数都是 2 2 2的相同幂次,最后多次开方即可.
代码
const int MAXN = 35;
int n;
int pow2[MAXN];
vii factor; // n的素因数分解
void init() {
pow2[0] = 1;
for (int i = 1; i <= 31; i++) pow2[i] = pow2[i - 1] << 1;
}
void solve() {
init();
cin >> n;
int maxs = 0; // 素因子的最大次数
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
int s = 0;
while (n % i == 0) n /= i, s++;
factor.push_back({ i,s });
maxs = max(maxs, s);
}
}
if (n > 1) factor.push_back({ n,1 });
int target = *lower_bound(pow2, pow2 + 32, maxs);
int ans = 1;
int cnt = log2(target);
bool different = false; // 记录是否存在与target不相等的次数
for (auto [u, v] : factor) {
ans *= u;
different |= v != target;
}
cout << ans << ' ' << cnt + different;
}
int main() {
solve();
}
1091C. New Year and the Sphere Transmission
原题指路:https://codeforces.com/problemset/problem/1091/C
题意
n ( 2 ≤ n ≤ 1 e 9 ) n\ \ (2\leq n\leq 1\mathrm{e}9) n (2≤n≤1e9)个人围成一圈,顺时针依次编号 1 ∼ n 1\sim n 1∼n.现选定一个正整数 k ( 1 ≤ k ≤ n ) k\ \ (1\leq k\leq n) k (1≤k≤n),初始时编号为 1 1 1的人手中有一个球,每次手中有球的人将把球递给其后顺时针第 k k k人.当球回到编号为 1 1 1的人手中时游戏结束.如 n = 6 , k = 4 n=6,k=4 n=6,k=4时,拿到球的序列为 1 , 5 , 3 , 1 1,5,3,1 1,5,3,1.定义该序列的权值为拿到球的人的编号之和(重复的只计一次),如上述序列的权值为 1 + 5 + 3 = 9 1+5+3=9 1+5+3=9.
对所有可能的 k k k,分别求拿到球的序列的权值,升序输出所有可能权值.可以证明可能的权值不超过 1 e 5 1\mathrm{e}5 1e5种.
思路
显然合法的 k k k是 n n n的约数.对每个 n n n的约数 d d d,将得到的序列升序排列后是等差数列,其首项 a 1 = 1 a_1=1 a1=1,公差为 d d d,项数为 n d \dfrac{n}{d} dn,则该序列的权值为 n d + n d ( n d − 1 ) d 2 \dfrac{n}{d}+\dfrac{\dfrac{n}{d}\left(\dfrac{n}{d}-1\right)d}{2} dn+2dn(dn−1)d.
代码
void solve() {
int n; cin >> n;
set<ll> ans;
for (int d = 1; (ll)d * d <= n; d++) {
if (n % d == 0) {
ans.insert((ll)n / d * (n / d - 1) * d / 2 + n / d);
if (d != n / d) ans.insert((ll)d * (d - 1) * n / d / 2 + d);
}
}
for (auto i : ans) cout << i << ' ';
}
int main() {
solve();
}
1110C. Meaningless Operations
原题指路:https://codeforces.com/problemset/problem/1110/C
题意
有 t ( 1 ≤ t ≤ 1000 ) t\ \ (1\leq t\leq 1000) t (1≤t≤1000)组测试数据.每组测试数据给定一个整数 a ( 2 ≤ a ≤ 2 25 − 1 ) a\ \ (2\leq a\leq 2^{25}-1) a (2≤a≤225−1).对所有的 b ∈ [ 1 , a − 1 ] b\in[1,a-1] b∈[1,a−1],求 gcd ( a ⊕ b , a & b ) \gcd(a\oplus b,a\&b) gcd(a⊕b,a&b)的最大值.
思路
①若 a = 2 x − 1 a=2^x-1 a=2x−1,注意到 gcd ( a ⊕ b , a & b ) = gcd ( 2 x − 1 − b , b ) = gcd ( 2 x − 1 , b ) \gcd(a\oplus b,a\&b)=\gcd(2^x-1-b,b)=\gcd(2^x-1,b) gcd(a⊕b,a&b)=gcd(2x−1−b,b)=gcd(2x−1,b),
故 gcd ( a ⊕ b , a & b ) \gcd(a\oplus b,a\&b) gcd(a⊕b,a&b)的最大值即 a a a最大的非平凡约数.
②若 a ≠ 2 x − 1 a\neq 2^x-1 a=2x−1,考察 b = ( 2 x − 1 ) ⊕ a b=(2^x-1)\oplus a b=(2x−1)⊕a.显然 b ∈ [ 1 , a − 1 ] b\in[1,a-1] b∈[1,a−1],且 a & b = 0 a\&b=0 a&b=0.
故 gcd ( 2 x − 1 , 0 ) = 2 x − 1 \gcd(2^x-1,0)=2^x-1 gcd(2x−1,0)=2x−1,其中 x x x是 a a a的MSB的下标(下标从 0 0 0开始).
代码
const int MAXN = 35;
int pow2[MAXN];
void init() {
pow2[0] = 1;
for (int i = 1; i <= 31; i++) pow2[i] = pow2[i - 1] << 1;
}
void solve() {
int a; cin >> a;
if (lowbit(a + 1) == a + 1) {
for (int i = 2; i * i <= a; i++) {
if (a % i == 0) {
cout << a / i << endl;
return;
}
}
cout << 1 << endl; // a是素数
}
else cout << *upper_bound(pow2, pow2 + 32, a) - 1 << endl;
}
int main() {
init();
CaseT // 单测时注释掉该行
solve();
}