写在前面

这篇博客记录一下这些天来做(和看到)的思维题,昨天是越做越有趣,越做越起劲(其实是奶茶喝多了睡不着),直接导致了我又一不小心想出了一些(可能)很鬼酷的思维题。

持续更新中……

CodeForces 892D Gluttony

传送门

  • 题目大意

给你nn不同数字组成的数组aa,现在再任取kk个数{x1,x2,,xk}(1xin,0<k<n)\{x_1, x_2,\dots, x_k\} (1 \leq x_i \leq n, 0 < k < n)

要你求出aa数组的某种排列bb,满足:i=1kaxii=1kbxi\displaystyle\sum_{i = 1}^{k}{a_{x_i}} \neq \displaystyle\sum_{i = 1}^{k}{b_{x_i}}

其中1n22,1ai1091 \leq n \leq 22,1 \leq a_i \leq 10^9

  • 题解思路

初看题目,觉得nn很小,或许是暴力?但是(22!)2(22!)^2有点大…时间上肯定过不去了。

于是转换思路,由于答案并不唯一,所以我是不是有办法构造出这样一个bb数组,使得i=1kbxi\displaystyle\sum_{i = 1}^{k}{b_{x_i}}尽可能地大于(或小于)i=1kaxi\displaystyle\sum_{i = 1}^{k}{a_{x_i}}

这么一想,这个bb似乎很好构造?假设aia_iaa中的第kk小,那么只要让对应的bib_iaa的第k+1k+1小不就好了(当然aa中的最大值要让bb中对应的数是aa中的最小值)?

这样能保证不取到aa中最大值的情况下,任取kk个数{x1,x2,,xk}(1xin,0<k<n)\{x_1, x_2,\dots, x_k\} (1 \leq x_i \leq n, 0 < k < n),均满足i=1kaxi<i=1kbxi\displaystyle\sum_{i = 1}^{k}{a_{x_i}} < \displaystyle\sum_{i = 1}^{k}{b_{x_i}}

如果取到aa的最大值能保证满足题目要求吗?答案是肯定的:

假设上述未取到的nkn-k个数分别为{xk+1,xk+2,,xn}\{x_{k+1}, x_{k+2},\dots, x_n\},由于i=1nai=i=1nbi\displaystyle\sum_{i = 1}^{n}{a_{i}} = \displaystyle\sum_{i = 1}^{n}{b_{i}},所以必然有i=k+1naxi>i=k+1nbxi\displaystyle\sum_{i = k+1}^{n}{a_{x_i}} > \displaystyle\sum_{i = k+1}^{n}{b_{x_i}}

至此,我们也就得出一个我们想要的bb了,同时证明了答案必然存在。

  • AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 30;
set<int> st;
int a[MAX_N], b[MAX_N];
int main() {
int n;
while(~ scanf("%d", &n)) {
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]), st.insert(a[i]);
auto ed = st.end();
--ed;
for(int i = 1; i <= n; ++i) {
auto it = st.lower_bound(a[i]);
if(it != ed)
b[i] = *(++it);
else b[i] = *st.begin();
}
for(int i = 1; i < n; ++i) printf("%d ", b[i]);
printf("%d\n", b[n]);
st.clear();
}
return 0;
}

CodeForces 763A Timofey and a tree

传送门

  • 题目大意

给你一棵有nn个节点的树,每个节点都有染上某种颜色cic_i,问你是否能找到一个根节点,使得其所拥有的子树11、子树22、……、子树pp(即所有子树),满足各个子树的所有节点的颜色相同,即子树11里所有节点值都相同,子树22、……、子树pp也是同理,假设这些值是a1,a2,,apa_1, a_2, \dots, a_p,它们可以相同,也可以不同。

其中2n105,1ci1052 \leq n \leq 10^5,1 \leq c_i \leq 10^5

  • 题解思路

这题其实蛮烧脑的,首先题意就有点难以理解,其次……画了很久的图才想出一个做法。

初看想到的是直接DFS走一遍,但是时间复杂度高达O(n2)O(n^2),显然不可取。

于是转换思路,开始去画图,偶然发现,在子树节点的颜色与这个根节点均不相同的前提下,满足题目条件的根节点必然满足:与该点连接的子树个数 = 与该点连接但颜色不同的点数。

那么顺着这个思路,我们只要求出所有两点颜色不同的的边数(这里假设为mm),再求出每个的点与其相连的点中有多少个颜色不同的点(这里假设为did_i),最终只要找到一个点xx满足dx=md_x = m即可。

证明如下:

如果现在找到了这样一个根节点xx,假设现在又在某一子树kk上修改某个节点yy的颜色,使得这个节点颜色与这棵子树的颜色以及根节点颜色都不同,这样会使得原本的m+1m + 1,致使xx无法成为我们想要的答案,而子树kk上原本所有的点(除了与xx相邻的那个点)的dd值均为00,这个操作只不过是让yy及其相连点的dd+1+1,由于m>0m > 0,所以这些点也就不会成为新解。

只有一种特殊情况:当上述操作的这个yy是与xx相邻的点,且m=1m=1(即这棵树是条链)时,修改这个yy值会使得yy成为新解,但根据题意可知,yy没修改前也是可行答案(但不是上述前提的解)之一。

注意:这里如果有解,那么只会有一个解,想想为什么?

但是能找出来的这个根节点又不一定与子树节点的颜色不同……这样做可以吗?

答案是肯定的。

找出的根节点与某个子树节点相同时,他们之间相连的边既不会被统计到mm,也不会统计到这两个点的dd值里,所以对于答案是不影响的。

换言之,如果整棵树的颜色都相同,那么树上任意点都是我们想要的答案啦。

  • AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<bits/stdc++.h>
using namespace std;
const int MAX_N = 1e5 + 10;
int p[MAX_N], d[MAX_N];
struct Edge {
int x, y;
}edge[MAX_N];
int main() {
int n, m, u, v;
while(~ scanf("%d", &n)) {
memset(d, 0, sizeof(d));
for(int i = 1; i < n; ++i) {
scanf("%d %d", &u, &v);
edge[i].x = u, edge[i].y = v;
}
for(int i = 1; i <= n; ++i)
scanf("%d", &p[i]);
m = 0;
for(int i = 1; i < n; ++i) {
if(p[ edge[i].x ] != p[ edge[i].y ])
++d[ edge[i].x ], ++d[ edge[i].y ], ++m;
}
bool flag = false;
for(int i = 1; i <= n; ++i)
if(d[i] == m) {
u = i;
flag = true;
break ;
}
if(flag) printf("YES\n%d\n", u);
else printf("NO\n");
}
return 0;
}

LightOJ 1095 math

传送门

  • 题目大意

给你一个nn个数字的初始序列(即11 ~ nn的升序排序),让你求在前mm个数中固定任意kk个数的位置,剩下的数字任意排列,可以生成多少种不同的序列?求答案取模 109+710^9+7 后的结果。

其中1n1000,0mn,0<km1 \leq n \leq 1000,0 \leq m \leq n,0 < k \leq m

  • 题解思路

这个题目其实是很明显的组合数学题,放到这个博客里主要是这个思维略微巧妙。

观察到题目要求在前mm个数中取kk个数固定位置,那么另外mkm-k个数必然是要错排的,至于后面的nmn-m个数,他们要不要错排都不要紧,因为n1000n \leq 1000,我们可以直接暴力枚举后面有ii个s数进行错排,然后算出总和,即:

Ans=Cmki=0k(Cnmidnki)Ans = C_m^k * \displaystyle\sum_{i=0}^k {(C_{n-m}^i * d_{n-k-i})}

其中CmnC_m^n代表在mm个物品中取nn个的组合数,did_i表示ii个不同元素的错排数。

注意预处理组合数和错排数,以及数字范围(会爆int)。

当然由于取模数是质数,也可以采用逆元的方式算组合数,这里提供的是普通的预处理方式。

  • AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll MOD = 1e9 + 7;
const int MAX_N = 1010;
ll C[MAX_N][MAX_N], d[MAX_N];
void init() {
d[0] = 1, d[1] = 0, d[2] = 1;
for(int i = 3; i < MAX_N; ++i)
d[i] = (i - 1) * (d[i - 1] + d[i - 2]) % MOD;
for(int i = 0; i < MAX_N; ++i)
C[i][0] = C[i][i] = 1;
for(int i = 1; i < MAX_N; ++i)
for(int j = 1; j < i; ++j)
C[i][j] = (C[i-1][j-1] + C[i-1][j]) % MOD;
}
int main() {
int T, n, m, k, Case = 0;
ll ans;
init();
scanf("%d", &T);
while(T--) {
ans = 0;
scanf("%d %d %d", &n, &m, &k);
for(int i = 0; i <= n-m; ++i)
ans = (ans + C[n-m][i] * d[n-k-i] % MOD) % MOD;
ans = ans * C[m][k] % MOD;
printf("Case %d: %lld\n", ++Case, ans);
}
return 0;
}

NOIP2017 Day1 T1 小凯的疑惑

传送门(洛谷)

  • 题目大意

给你两个正整数aabb,保证两个数互质。问这两个数最大不能组成的数是多少?

其中1a,b1091 \leq a, b \leq 10^9

  • 题解思路

不得不说这D1T1可能是我见过最难想到的题目了……除了打表找规律我几乎没有任何太大想法,想想当初要是复读一年遇到这题可能连个60分都拿不到?

但其实是道数论题,摘录网上某dalao的解释:

不妨设a<ba < b,所求答案为xx
若有xma(modb)(1mb1)x \equiv ma \pmod{b} (1 \leq m \leq b-1),即x=ma+nb(1mb1)x = ma + nb (1 \leq m \leq b-1)
显然当n0n \geq 0时,xx必然可以用a,ba, b表示出来,不符合题意。
因此nn最大值为1-1,此时x=mabx = ma - b,再观察上述范围,当m=b1m = b-1时,xx取得最大值。

Ans=(b1)ab=abab\therefore Ans = (b-1) * a - b = a * b - a - b

  • AC代码

1
2
3
4
5
6
7
8
9
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int main() {
ll a, b;
cin >> a >> b;
cout << a * b - a - b << endl;
return 0;
}

BZOJ 3884 上帝与集合的正确用法

传送门

  • 题目大意

2222modp\underbrace{2^{2^{2^{\sdot^{\sdot^{\sdot^{2}}}}}}}_\infty \mod p

其中p107p \leq 10^7

  • 题解思路

乍一看很难,实际上只是道普通的欧拉降幂应用题。

在数学上,把形如aaaan\underbrace{a^{a^{a^{\sdot^{\sdot^{\sdot^{a}}}}}}}_n称为aann次迭代乘方,或者aann次超乘方……,具体可以查阅相关资料,一般写作n ⁣a{}^n\!a,特别地,0 ⁣a=1{}^0\!a = 1

那么我们可以设f(p)f(p)表示 ⁣2modp{}^\infty\!2 \mod p,由于 ⁣2{}^\infty\!2必然大于φ(p)\varphi(p)

则根据欧拉降幂公式可得f(p)={2f(φ(p)),gcd(2,p)=12f(φ(p))+φ(p),gcd(2,p)1modpf(p) = \begin{cases} 2^{f(\varphi(p))}, & gcd(2, p) = 1 \\ 2^{f(\varphi(p)) + \varphi(p)}, & gcd(2, p) \not = 1 \end{cases} \mod p

这里的φ(p)\varphi(p)指的是欧拉函数。又显而易见的,当p=1p = 1时,f(p)=0f(p) = 0

这样我们就得到了一个时间复杂度接近O(log2p)O(\log_2{p})的算法,这个时候我们可以考虑筛10710^7内的欧拉函数或者每一次递归都求一次欧拉函数。前者时间复杂度O(p+log2p)O(p+\log_2{p}),后者O(plog2p)O(\sqrt{p}\log_2{p})

  • AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int MAX_N = 1e7 + 10;
int CNT, prime[MAX_N], phi[MAX_N];
bool vis[MAX_N];
void make_prime_phi() {
CNT = 0;
for(int i = 2; i < MAX_N; ++i) {
if(!vis[i]) {
prime[++CNT] = i;
phi[i] = i-1;
}
for(int j = 1; j <= CNT && i*prime[j] < MAX_N; ++j) {
vis[ i*prime[j] ] = true;
if(i % prime[j] == 0) {
phi[ i*prime[j] ] = phi[i] * prime[j];
break ;
} else phi[ i*prime[j] ] = phi[i] * (prime[j]-1);
}
}
}

ll gcd(ll a, ll b) {
return b ? gcd(b, a%b) : a;
}

ll qmod(ll a, ll n, ll MOD) {
ll ans = 1LL;
for(; n; n >>= 1, a = a*a % MOD)
if(n & 1) ans = ans*a % MOD;
return ans;
}

ll f(ll p) {
if(p == 1) return 0LL;
ll d = gcd(2, p), tmp = f(phi[p]);
if(d != 1) tmp += phi[p];
return qmod(2, tmp, p);
}

int main() {
make_prime_phi();
int T;
ll p;
scanf("%d", &T);
while(T--) {
scanf("%lld", &p);
printf("%lld\n", f(p));
}
return 0;
}

CodeForces 977D Divide by three, multiply by two

传送门

  • 题目大意

有个人任取一个数字xx,随机地进行n1n-1次下面两种操作:

  1. xx除以33,前提是xmod3=0x \mod 3 = 0
  2. xx22

这样能得到nn个数字,现在给你这nn个数字的某种排列,让你还原出一种满足上述两种操作的排列。即题目要求的序列满足:若第i(i<n)i(i < n)个数为xix_i,则xi+1x_{i+1}必须是xi/3x_i / 32xi2 \cdot x_i

题目保证答案一定存在,其中2n1002 \leq n \leq 1001xi310181 \leq x_i \leq 3 \cdot 10^{18}

  • 题解思路

根据题意易证,这nn个数都是不相同的。

所以有一种做法是深搜,任意枚举一个数字xx,按它的操作搜索可以排在这个数字后面的数,由于题目保证有解,所以易知满足条件的数只有一个,假设为yy,再用yy不断深搜下去。最后用同样的方法去搜索xx前面的数,最后必然得到一个满足题意的序列。

时间复杂度大概在O(n)O(n2)O(n) - O(n^2),对于这个题目都可以过。

这里提供一个更加巧妙的做法(这样才符合头脑风暴专题嘛)

既然题目保证答案一定存在,那么我们可以假设xi=3ji2kipx_i = 3^{j_i} \cdot 2^{k_i} \cdot p,其中pp可能是任意非负整数。

xi+1=3ji+12ki+1px_{i+1} = 3^{j_{i+1}} \cdot 2^{k_{i+1}} \cdot p必然满足ji+1jij_{i+1} \leq j_iki+1kik_{i+1} \geq k_i,那么我们只要按照这样的规则去对原有序列进行排序,就能找到一个满足题目要求的序列。

即假设现在有两个数a,ba, b,若aa所含的33因子较多,aa的优先级就较高,否则去比较所含22的因子数,较少的优先级较高(实际上就是较小的数优先级较高)。

事实上,上面这个做法恰巧证明了,满足题目的解有且只有一种。

  • AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <cstdio>
#include <algorithm>
using namespace std;

#define ll long long
const int MAX_N = 110;
ll a[MAX_N];

inline int get_cnt(ll x) {
int ret = 0;
while(x % 3 == 0)
++ret, x /= 3;
return ret;
}

bool cmp(ll x, ll y) {
int cnt1 = get_cnt(x), cnt2 = get_cnt(y);
if(cnt1 == cnt2)
return x < y;
return cnt1 > cnt2;
}

int main() {
int n, i, k;
ll now, tmp;
scanf("%d", &n);
for(i = 1; i <= n; ++i)
scanf("%lld", &a[i]);
sort(a + 1, a + 1 + n, cmp);
for(i = 1; i < n; ++i) printf("%lld ", a[i]);
printf("%lld\n", a[n]);
return 0;
}

计蒜客 41229 super_log

传送门

  • 题目大意

定义一个函数如下:

loga(x)={1,x<11+loga(logax),x1\log_a^*(x) = \begin{cases} -1, &{x < 1} \\ 1 + \log_a^*(\log_a{x}), &{x \geq 1} \end{cases}

给你两个正整数aabb,让你求一个最小的xx满足loga(x)b\log_a^*(x) \geq b,答案可能很大,输出xmodmx \mod m的结果即可。

特别地,当a=1a = 1时,这个xx的最小解是11

其中,a,m[1,106],b[0,106]a, m \in [1, 10^6], b \in [0, 10^6]

  • 题解思路

这是2019年ICPC亚洲区域赛南京站网络选拔赛的题目。

不难得出,函数loga(x)\log_a^*(x)实际上是在求最大的nn满足n ⁣ax{}^n\!a \leq x,那么对于题目来说,找一个最小的xx满足loga(x)b\log_a^*(x) \geq b就是b ⁣a{}^b\!a。所以题目是在求b ⁣amodm{}^b\!a \mod m

显而易见地,又是一个欧拉降幂题(逃

f(a,n,p)=n ⁣amodpf(a, n, p) = {}^n\!a \mod p,则根据欧拉降幂公式得

f(a,n,p)={af(a,n1,φ(p)),gcd(a,p)=1n ⁣a,gcd(a,p)1,(n1 ⁣a)<paf(a,n1,φ(p))+φ(p),gcd(a,p)1,(n1 ⁣a)pf(a, n, p) = \begin{cases} a^{f(a, n-1, \varphi(p))}, & {\gcd(a, p) = 1} \\ {}^n\!a, & {\gcd(a, p) \not = 1, ({}^{n-1}\!a) < p} \\ a^{f(a, n-1, \varphi(p)) + \varphi(p)}, & {\gcd(a, p) \not = 1,({}^{n-1}\!a) \geq p} \end{cases}

特别地,f(a,0,p)=f(1,n,p)=1%pf(a, 0, p) = f(1, n, p) = 1 \% pf(a,n,1)=0f(a, n, 1) = 0

现在的问题是如何判断ppn1 ⁣a{}^{n-1}\!a的大小,这个其实很简单,既然我们知道题目给的函数loga(x)\log_a^*(x)是求最大的nn满足n ⁣ax{}^n\!a \leq x,那么我们只要判断loga(p)\log_a^*(p)n1n-1的大小就可以了。

  • AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <bits/stdc++.h>
using namespace std;

#define ll long long
const ll MAX_N = 1000000 + 10;
ll CNT, prime[MAX_N], phi[MAX_N];
bool vis[MAX_N];

void make_prime_phi() {
CNT = 0;
memset(vis, false, sizeof(vis));
for(ll i = 2; i < MAX_N; ++i) {
if(!vis[i]) {
prime[++CNT] = i;
phi[i] = i-1;
}
for(ll j = 1; j <= CNT && i*prime[j] < MAX_N; ++j) {
vis[ i*prime[j] ] = true;
if(i % prime[j] == 0) {
phi[ i*prime[j] ] = phi[i] * prime[j];
break ;
} else phi[ i*prime[j] ] = phi[i] * (prime[j]-1);
}
}
}

ll qmod(ll a, ll n, ll MOD)
{
ll ans = 1;
for(; n; n >>= 1, a = a*a % MOD)
if(n & 1) ans = ans*a % MOD;
return ans;
}

ll get_log(ll x, ll a) {
if(x < 1) return -1;
return 1 + get_log(log(x)/log(a), a);
}

ll gcd(ll a, ll b) {
return b ? gcd(b, a%b) : a;
}

ll f(ll a, ll n, ll p) {
if(a == 1 || n == 0) return 1%p;
if(p == 1) return 0;
if(n == 1) return a % p;
if(gcd(a, p) == 1) {
return qmod(a, f(a, n-1, phi[p]), p);
} else {
ll k = get_log(phi[p], a);
if(k <= n-1)
return qmod(a, f(a, n-1, phi[p]) + phi[p], p);
else return qmod(a, f(a, n-1, phi[p]), p);
}
}

int main() {
make_prime_phi();
int T; ll a, b, m, ans;
scanf("%d", &T);
while(T--) {
scanf("%lld %lld %lld", &a, &b, &m);
printf("%lld\n", f(a, b, m) % m);
}
return 0;
}

CodeFoces 849A Odds and Ends

传送门

  • 题目大意

给你nn个数字组成的数组aa,问你能否分出奇数个长度为奇数的子区间,且保证每个子区间的开头和末尾都是奇数。

其中1n100,0ai1001 \leq n \leq 100, 0 \leq a_i \leq 100

  • 题解思路

这个题目很容易让人想到搜索,但实际上没有那么麻烦。

首先我们知道,奇数个奇数相加(即奇数乘奇数)必然是个奇数。那么如果nn为偶数,就一定无法分出奇数个奇数长度的子区间。

再者,因为11是奇数,所以如果这个数组aa的开头和末尾都是奇数,必然是满足条件的。

最后,如果数组aa的开头和末尾有一个不是奇数,那么分出来的子区间必然第一个的开头不是奇数或最后一个的末尾不是奇数,不满足条件。

所以我们只要判断nn和数组aa的开头末尾是否均为奇数就可以了。

  • AC代码

1
2
3
4
5
6
7
8
9
10
11
12
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, a[110];
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
if((n&1) && (a[1]&1) && (a[n]&1))
printf("Yes\n");
else printf("No\n");
return 0;
}

CodeForces 872B Maximum of Maximums of Minimums

传送门

  • 题目大意

给你nn个数组成的数组aa,要你分成连续的kk段,取每段的最小值,得到的kk个数中取最大值,我们假设这个值为AnsAns,问这个AnsAns最大能得到多少?

其中1kn1051 \leq k \leq n \leq 10^5ai[109,109]a_i \in [-10^9, 10^9]

  • 题解思路

看数据范围就知道,这题显然不能用搜索(

首先考虑k=1k = 1的情况,显然答案就是整个数组里的最小值。

又发现如果k3k \geq 3,那么一定可以将整个数组中的最大值作为一个独立的段,那么答案显而易见的是这个最大值。

现在要考虑的是k=2k = 2的情况,可以枚举一个点ii,将数组分为a1a_1aia_iai+1a_{i+1}ana_n两段,也就是只要取区间[1,i][1, i]的最小值和区间[i+1,n][i+1, n]的最小值中的较大值,最后在所有情况里取最大值。用线段树或者其他数据结构维护的话,时间复杂度大概在O(nlog2n)O(n\log_2n)

这样这个题目就迎刃而解了,不过这里提供一个对于k=2k = 2的情况更优的做法。

通过观察其实不难发现,k=2k = 2时,答案必然是a1a_1ana_n两个数中的较大值。

证明如下:

先假设答案不是a1a_1ana_n,那么说明在区间[2,n1][2, n-1]之间会有一个数(这里假设为axa_x)大于这两个数,并且在分成的两段区间的某一段内是最小值。但是由于只分成两段,a1a_1ana_n必然会有一个和axa_x在同一段内,那么axa_x就不会是这段区间的最小值,与假设条件矛盾。

  • AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <bits/stdc++.h>
using namespace std;

const int MAX_N = 1e5 + 10;
int a[MAX_N];

int main() {
int n, k, ans;
scanf("%d %d", &n, &k);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
if(k == 1) {
ans = a[1];
for(int i = 2; i <= n; ++i)
ans = min(a[i], ans);
} else if(k == 2) {
ans = max(a[1], a[n]);
} else {
ans = a[1];
for(int i = 2; i <= n; ++i)
ans = max(a[i], ans);
}
printf("%d\n", ans);
return 0;
}

CodeForces 876B Divisiblity of Differences

传送门

  • 题目大意

给你nn个数字组成数组aa,问你能否在这些数字中找到kk个数,使得这kk个数两两相差的值对mm取余数均为00,如果可以,输出这kk个数。

其中,2kn105,1m105,0a1092 \leq k \leq n \leq 10^5, 1 \leq m \leq 10^5, 0 \leq a \leq 10^9

  • 题解思路

通过观察可以发现若两个数相差的值取模某个数mm00,那么这两个数取模mm得到的值必定相同,所以只要判断这nn个数中是否存在至少kk个数模mm为同一个数即可,最后输出满足条件的kk个数。

  • AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <bits/stdc++.h>
using namespace std;

const int MAX_N = 1e5 + 10;
int a[MAX_N], cnt[MAX_N];

int main() {
memset(cnt, 0, sizeof(cnt));
int n, k, m, pi;
bool flag = false;
scanf("%d %d %d", &n, &k, &m);
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
++cnt[a[i] % m];
}
for(int i = 0; i < m; ++i) {
if(cnt[i] >= k) {
pi = i;
flag = true;
break ;
}
}
if(flag) {
printf("Yes\n");
for(int i = 1, j = 1; i <= k && j <= n; ++j) {
if(a[j] % m == pi) {
if(i == k) printf("%d\n", a[j]);
else printf("%d ", a[j]);
++i;
}
}
} else printf("No\n");
return 0;
}

快跑啊,JV又来抓人了!

  • 题目大意

这是我自己出给新生做的题,挂在学校OJ,暂时没开放题目页面,所以没有传送门啦。

题意很简单, 给你1n1051 \leq n \leq {10}^5个数字a1ana_1 \dots a_n,再给你一个数字xx,求i=1nai\displaystyle\prod_{i=1}^{n }a_ixx的最小公倍数mod(109+7)\mod (10^9 + 7),其中1x,ai<2631 \leq x, a_i < 2^{63}

  • 题解思路

这题是我某天无聊看陈景润写的《初等数论Ⅰ》,恰巧想出来的小题目,对于老法师来说应该很简单吧。

众所周知,最小公倍数lcm(a,b)=ab÷gcd(a,b)lcm(a, b) = a \cdot b \div gcd(a, b),但因为有除法不能乱取模,所以要换种思路,即想办法找到i=1nai\displaystyle\prod_{i=1}^{n }a_ixx的最大公约数,然后让xx除以它,这里提供两种方法:

  1. 由于gcd(a,b)=gcd(b,amodb)gcd(a, b) = gcd(b, a \mod b),那么我们便可以先求i=1naimodx\displaystyle\prod_{i=1}^{n }a_i \mod x ,这里可以利用__int128(如果编译器支持的话),或是写个高精度乘法和高精度取模求。

  2. 对于gcd(ab,x)gcd(a \cdot b, x),易知其因子里必然含有gcd(a,x)gcd(a, x),那么让xx除以gcd(a,x)gcd(a, x)后再与bb取最大公约数,再乘上gcd(a,x)gcd(a, x)就得到了gcd(ab,x)gcd(a \cdot b, x),即我们可以拆分成gcd(a,x)gcd(b,x÷gcd(a,x))gcd(a, x) \cdot gcd(b, x \div gcd(a, x)),推广到nn个数的话就是gcd(i=1nai,x)=gcd(i=1n1ai,x)gcd(an,x÷gcd(i=1n1ai,x))gcd(\displaystyle\prod_{i=1}^{n }a_i, x) = gcd(\displaystyle\prod_{i=1}^{n-1 }a_i, x) \cdot gcd(a_n, x \div gcd(\displaystyle\prod_{i=1}^{n-1 }a_i, x)),这样我们就得到了一个递推式,便可以在O(n)O(n)的时间复杂度内求出x÷gcd(i=1nai,x)x \div gcd(\displaystyle\prod_{i=1}^{n }a_i, x)

  • AC代码

    • 方法1:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    #include <bits/stdc++.h>

    using namespace std;

    #define ll long long

    const ll MOD = 1e9 + 7;
    const int MAX_N = 1e6 + 10;

    template <class T>
    T gcd(T a, T b) {
    return b ? gcd(b, a % b) : a;
    }

    ll a[MAX_N];

    int main() {
    int n;
    ll x;
    while (~scanf("%d %lld", &n, &x)) {
    for (int i = 1; i <= n; ++i)
    scanf("%lld", &a[i]);
    __int128 tmp = a[1];
    for (int i = 2; i <= n; ++i) {
    tmp = tmp * a[i] % x;
    }
    tmp = gcd(tmp, (__int128)x);
    x /= (ll)tmp;
    tmp = a[1];
    for (int i = 2; i <= n; ++i) {
    tmp = tmp * a[i] % MOD;
    }
    tmp = tmp * x % MOD;
    printf("%lld\n", (ll)tmp);
    }
    return 0;
    }
    • 方法2:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    #include <bits/stdc++.h>

    using namespace std;

    #define ll long long

    const ll MOD = 1e9 + 7;
    const int MAX_N = 1e6 + 10;

    template <class T>
    T gcd(T a, T b) {
    return b ? gcd(b, a % b) : a;
    }

    ll a[MAX_N];

    int main() {
    int n;
    ll x, ans, r;
    while (~scanf("%d %lld", &n, &x)) {
    ans = 1LL;
    for (int i = 1; i <= n; ++i) {
    scanf("%lld", &a[i]);
    r = gcd(x, a[i]);
    x /= r;
    a[i] %= MOD;
    ans = (ans * a[i]) % MOD;
    }
    ans = ans * (x % MOD) % MOD;
    printf("%lld\n", ans);
    }
    return 0;
    }