Featured image of post 洛谷

洛谷

线性筛素数

题目描述 如题,给定一个范围 n,有 q 个询问,每次输出第 k 小的素数。

输入格式 第一行包含两个正整数 n,q,分别表示查询的范围和查询的个数。

接下来 q 行每行一个正整数 k,表示查询第 k 小的素数。

输出格式 输出 q 行,每行一个正整数表示答案。

示例 1:

输入 #1复制 100 5 1 2 3 4 5 输出 #1复制 2 3 5 7 11

代码

 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
#include<stdio.h>
#include<string.h>

int main() {
    int n, q, a;
    int x[1001000];
    bool y[100100000] = {0};
    int t = 0;

    scanf("%d %d", &n, &q);

    for (int i = 2; i <= n; i++) {
        if (!y[i]) {
            x[t++] = i;
        }
        for (int j = 0; j < t && i * x[j] <= n; j++) {
            y[i * x[j]] = 1;
            if (i % x[j] == 0) break;
        }
    }

    for (int i = 0; i < q; i++) {
        scanf("%d", &a);
        if (a <= 0 || a > t) {
            printf("Invalid input\n");
        } else {
            printf("%d\n", x[a - 1]); // 改成0-based索引
        }
    }

    return 0;
}

解法

欧拉筛,线性筛,总体思路是先筛2(最多两倍),然后读入一个素数,然后再筛2,3(最多3倍),然后读入一个素数,这样一直添加。可以保证添加的一定为素数

模板 快速幂

给你三个整数 a,b,p,求 a b mod p。

输入格式 输入只有一行三个整数,分别代表 a,b,p。

输出格式 输出一行一个字符串 a^b mod p=s,其中 a,b,p 分别为题目给定的值, s 为运算结果。

代码

 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<stdio.h>
#include <stdlib.h>
long long x[47483648] = {};
long long fx(long long a, long long b, long long p)
{
	long long lld, e;
	if (b == 1)
		return a % p;
	long long c = b / 2;
	if (c< 47483648&&x[c] != 0)
		lld = x[c];
	else
		lld = fx(a, c, p);
	if(b< 47483648)
	x[b] = lld * e % p;
	return lld * e % p;
}
int main()
{
	long long a, b, c, lld, p, s;
	scanf("%lld %lld %lld", &a, &b, &p);
	c = fx(a, b, p);
	printf("%lld^%lld mod %lld=%lld", a, b, p, c);
	return 0;
}

解法

一定要long long 不然中途回爆炸,将幂转换成二进制使用,维护一个答案,当幂上出现奇数的时候对答案乘上一个base,然后将base幂一下,幂指数b也除以二

括号配对II

高级语言程序设计中的各种括号应该匹配,例如:“(” 与 “)”匹配、“[”与 “]” 匹配、“{”与 “}” 匹配等。 输入一字符文件,判断其中的括号是否匹配。假设括号没有优先级差别。

输入格式: 多行,字符个数不超过 65536。

输出格式: 一个单词,表示字符文件中括号匹配的结果,匹配输出“yes”,否则输出“no”.

输入样例: 在这里给出一组输入。例如:

( { } ) {a=(b*c)+free( ) }。

代码

 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
#include <stdio.h>
char x[1000]={'#'};
int main(void)
{
	int a = 1, b = 1, c, d;
	char ch;
	while (scanf("%c",&ch)!=EOF)
	{
		if (ch == '(' || ch == '{' || ch == '[')
		{
			x[a] = ch;
			a++;
		}
		if (ch == ')')
		{
			if (x[a - 1] == '(' && a >= 1)
			{
				a--;
				x[a] = 0;
			}
			else
			{
				b = 0;
				break;
			}
		}
		if (ch == ']')
		{
			if (x[a - 1] == '[' && a >= 1)
			{
				a--;
				x[a] = 0;
			}
			else
			{
				b = 0;
				break;
			}
		}
		if (ch == '}')
		{
			if (x[a - 1] == '{'&&a>=1)
			{
				a--;
				x[a] = 0;
			}
			else
			{
				b = 0;
				break;
			}
		}
	}
	if (b == 0 || a != 1)
		printf("no");
	else
		printf("yes");
	return 0;
}

解法

使用栈结构,使用while (scanf("%c",&ch)!=EOF)来判断是否还有输入

Blah数集

大数学家高斯小时候偶然发现一种有趣的自然数集合Blah。以a为基的集合Ba定义如下:

a是集合Ba的基,且a是Ba的第一个元素; 若x在集合Ba中,则2x+1和3x+1也都在Ba中; 没有其它元素在集合Ba中。 现在小高斯想知道如果将集合Ba中元素按照升序排列,第n个元素会是多少? 输入格式: 多行,每行包括两个数,集合的基a(1<=a<=50))以及所求元素序号n(1<=n<=1000000)

输出格式: 对于每个输入,输出集合Ba的第n个元素值

输入样例: 在这里给出一组输入。例如:

1 5 25 100000

输出样例: 在这里给出相应的输出。例如:

9 34503679

代码

 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
#include <stdio.h>
long long x[1000000];
int main(void)
{
	long long a = 0, b = 0, c = 1, n, i;
	while (scanf("%lld %lld", &i, &n) != EOF)
	{
		x[0] = i;
		a = 0;
		b = 0;
		for (c = 1; c <= n; c++)
		{
			if ((x[a] * 2 + 1 == x[b] * 3 + 1))
			{
				x[c] = x[a] * 2 + 1;
				a++;
				b++;
			}
			else if (x[a] * 2 + 1 < x[b] * 3 + 1)
			{
				x[c] = x[a] * 2 + 1;
				a++;
			}
			else
			{
				x[c] = x[b] * 3 + 1;
				b++;
			}
		}
		printf("%lld\n", x[n-1]);
	}
}

解法

维护2x+1与3x+1两个指针,都大于已有的最后一个数,然后比较,添加进入。

左侧最近小数

对N个非负整数的序列,查询元素A i左侧最近的小于A i ​ 的整数(1≤i≤N),如果不存在,输出 -1

输入格式: 第1行,1个整数N,表示整数的个数,(1≤N≤100000)。

第2行,N个整数,每个整数x 都满足 0 ≤ x ≤2000000000。

输出格式: 1行,N个整数,表示每个元素A i ​ 左侧最近的小于A i ​ 的整数(1≤i≤N)。

代码

 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 <stdio.h>
long long x[100000] = {-1};
int main(void)
{
	long long a, b=0, c, d, n, i;
	scanf("%lld", &n);
	for (a = 0; a < n; a++)
	{
		scanf("%lld", &i);
		for (; x[b] >= i;b--)
		{
			x[b] = 0;
		}
		if (i > x[b])
		{
			if(n-a!=1)
			printf("%lld ", x[b]);
			else
				printf("%lld", x[b]);
			b++;
			x[b] = i;
		}
	}
}

解法

维护一个单调栈,从左往右读,每次跟最上边比,大就入栈,小就弹栈再比

区间最小值I

对N个整数的序列,从左到右连续查询 M 长度子序列 A i ​ ,A i+1 ​ ,…,A i−M+1 ​ (1≤i,M≤N)的最小值。

输入格式: 第1行,两个整数:N和M,表示整数的个数和区间长度,1≤N≤100000. 第2行,N个整数,每个整数x 都满足 │x│≤2000000000。

输出格式: 1行, 用空格分隔的N-M+1个整数,对应从左到右所有连续M长度子序列的最小值。

输入样例: 6 3 1 2 5 3 4 6 输出样例: 1 2 3 3 。

代码

 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
#include <stdio.h>
long long x[100000][2];
int main(void)
{
	long long n, m, a, b, c = 0, d = 0, i;
	scanf("%lld %lld", &n, &m);
	for (a = 0; a < n; a++)
	{
		scanf("%lld", &i);
		for (b = c; b >= 0; b--)    //单调性维护
		{
			if (b == d || i > x[b - 1][0])
			{
				x[b][0] = i;
				x[b][1] = a;
				c++;
				break;
			}
			else
			{
				x[b - 1][0] = 0;
				x[b - 1][1] = 0;
				c--;
			}
		}
		if (x[c - 1][1] - x[d][1] >= m)     //区间维护
		{
			x[d][1] = 0;
			x[d][0] = 0;
			d++;
		}
		if (a + 1 >= m)
		{
			if(n-a!=1)
			printf("%lld ", x[d][0]);
			else
				printf("%lld", x[d][0]);
		}
	}
}

解法

使用两个数组,一个存数字,一个存下标,一个移动的指针指向最小,只有递增的加入队列,左指针随着移动,分为单调性维护和区间维护

a+b高精度加法

高精度加法,相当于 a+b problem,不用考虑负数。

输入格式 分两行输入。a,b≤10 500 。

输出格式 输出只有一行,代表 a+b 的值。

代码

 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<stdio.h>
#include<string.h>
char a[10010];
int x[10010];
int main(){
	scanf("%s",&a);
	int n=strlen(a);
	for(int i=0;i<n;i++){
		x[i]+=a[n-1-i]-'0';
	}
	scanf("%s",&a);
	n=strlen(a);
	for(int i=0;i<n;i++){
		x[i]+=a[n-1-i]-'0';
	}
	for(int i=0;i<10000;i++){
		x[i+1]+=x[i]/10;
		x[i]=x[i]%10;

	}
	int t=0;
	for(int i=10010;i>=0;i--){
		if(x[i]!=0){
			t=1; 
		}
		if(t==1){
			printf("%d",x[i]);
		}
	}
	if(t==0){
		printf("0");
	}
	return 0;
} 

解法

用一个字符数组记录读入的数字,用一个数字数组翻转,再使用模10计算进位,最后从后往前打印,存在第一个非0的打印,0单独考虑

[NOIP 2015 提高组] 跳石头

一年一度的“跳石头”比赛又要开始了!

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)

输入格式 第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证 L≥1 且 N≥M≥0。

接下来 N 行,每行一个整数,第 i 行的整数 D i ​ (0<D i ​ <L), 表示第 i 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

输出格式 一个整数,即最短跳跃距离的最大值。

输入输出样例 输入 #1复制

25 5 2 2 11 14 17 21 输出 #1复制

4 说明/提示 输入输出样例 1 说明 将与起点距离为 2 和 14 的两个岩石移走后,最短的跳跃距离为 4(从与起点距离 17 的岩石跳到距离 21 的岩石,或者从距离 21 的岩石跳到终点)。

数据规模与约定 对于 20%的数据,0≤M≤N≤10。 对于 50% 的数据,0≤M≤N≤100。 对于 100% 的数据,0≤M≤N≤50000,1≤L≤10 9 。

代码

 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
#include<cstdio>

int k,n,m;
int pos[50005];

bool check(int mid){
    int ans=0,st=0;
    for(int i=1;i<=n;++i)
        if(pos[i]-st<mid)
            ans=ans+1;
        else st=pos[i];
    return ans<=m;
}

int main(){
    scanf("%d%d%d",&k,&n,&m);
    for(int i=1;i<=n;++i)scanf("%d",&pos[i]);
    int l=0,r=k,mid;
    while(l<r){
        mid=(l+r)>>1;
        if(check(mid))l=mid+1;
        else r=mid-1;
    }
    if(!check(l))l-=1;
    printf("%d\n",l);
    return 0;
}

解法

枚举答案,然后判断答案是否正确,对枚举的答案先进行判断,然后进行二分。

141. 【深基13.例1】查找

题目描述 输入 n 个不超过 10 9 的单调不减的(就是后面的数字不小于前面的数字)非负整数 a 1 ​ ,a 2 ​ ,…,a n ​ ,然后进行 m 次询问。对于每次询问,给出一个整数 q,要求输出这个数字在序列中第一次出现的编号,如果没有找到的话输出 −1 。

输入格式 第一行 2 个整数 n 和 m,表示数字个数和询问次数。

第二行 n 个整数,表示这些待查询的数字。

第三行 m 个整数,表示询问这些数字的编号,从 1 开始编号。

输出格式 输出一行,m 个整数,以空格隔开,表示答案。

输入输出样例 输入 #1复制

11 3 1 3 3 3 5 7 9 11 13 15 15 1 3 6 输出 #1复制

1 2 -1 说明/提示 数据保证,1≤n≤10 6 ,0≤a i ​ ,q≤10 9 ,1≤m≤10 5

代码

 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
#include<stdio.h>
#include<math.h>

int x[1001000];
int main(){
	int n,m,q;
	int l,r,mid,p;
	scanf("%d %d",&n,&m);
	for(int i=0;i<n;i++){
		scanf("%d",&x[i]);
	} 
	for(int i=0;i<m;i++){
		scanf("%d",&q);
		l=0;
		p=0;
		r=n-1;
		while(l<=r){
			mid=(l+r)/2;
			if(x[mid]==q){
				p=mid;
				r=mid-1;
			}
			else if(x[mid]>q){
				r=mid-1;
			}
			else if(x[mid]<q){
				l=mid+1;
			}
		}
		if(x[p]!=q){
			printf("-1 ");
			continue;
		}
		else{
			printf("%d ",p+1);
		}
	}
	return 0;
} 

解法

使用二分法,从中间开始判断,mid+1,mid-1

木材加工

题目背景 要保护环境

题目描述 木材厂有 n 根原木,现在想把这些木头切割成 k 段长度均为 l 的小段木头(木头有可能有剩余)。

当然,我们希望得到的小段木头越长越好,请求出 l 的最大值。

木头长度的单位是 cm,原木的长度都是正整数,我们要求切割得到的小段木头的长度也是正整数。

例如有两根原木长度分别为 11 和 21,要求切割成等长的 6 段,很明显能切割出来的小段木头长度最长为 5。

输入格式 第一行是两个正整数 n,k,分别表示原木的数量,需要得到的小段的数量。

接下来 n 行,每行一个正整数 L i ​ ,表示一根原木的长度。

输出格式 仅一行,即 l 的最大值。

如果连 1cm 长的小段都切不出来,输出 0。

输入输出样例 输入 #1复制

3 7 232 124 456 输出 #1复制

114 说明/提示 数据规模与约定 对于 100% 的数据,有 1≤n≤10 5 ,1≤k≤10 8 ,1≤L i ​ ≤10 8 (i∈[1,n])。

代码

 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
#include <stdio.h>

#define MAX_N 100100

long long x[MAX_N];

int main() {
    int n, k;
    scanf("%d %d", &n, &k);
    
    long long sum = 0;
    long long max_length = 0;
    
    // 读入木材长度,并计算总长度和最大长度
    for (int i = 0; i < n; i++) {
        scanf("%lld", &x[i]);
        sum += x[i];
        if (x[i] > max_length) {
            max_length = x[i];
        }
    }
    
    // 如果总长度不足以分成 k 段,直接返回 0
    if (sum < k) {
        printf("0\n");
        return 0;
    }
    
    long long low = 1;
    long long high = max_length;
    long long ans = 0;
    
    while (low <= high) {
        long long mid = (low + high) / 2;
        long long count = 0;
        
        // 计算以 mid 为长度可以切出多少段
        for (int i = 0; i < n; i++) {
            count += x[i] / mid;
            if (count >= k) break; // 提前退出优化
        }
        
        if (count >= k) {
            ans = mid;  // 当前长度可行,尝试更大的长度
            low = mid + 1;
        } else {
            high = mid - 1;
        }
    }
    
    printf("%lld\n", ans);
    
    return 0;
}

解法

倒着走,不去搜答案而是判断答案,然后通过二分法来加速过程

94. 二叉树的中序遍历

给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。

示例 1:

输入:root = [1,null,2,3] 输出:[1,3,2]

示例 2:

输入:root = [] 输出:[]

示例 3:

输入:root = [1] 输出:[1]

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Solution {
public:
    void inorder(TreeNode* root, vector<int>& res)
    {
        if (root==nullptr) {
            return;
        }
        inorder(root->left,res);
        res.push_back(root->val);
        inorder(root->right,res);
    }
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        inorder(root,res);
        return res;
    }
};

解法

传入一个栈,用res.push_back(root->val);压入

104. 二叉树的最大深度

给定一个二叉树 root ,返回其最大深度。

二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。

示例 1:

输入:root = [3,9,20,null,null,15,7] 输出:3

示例 2:

输入:root = [1,null,2] 输出:2

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
    int deep (TreeNode* root,int d)
    {
        if(root==nullptr)
        {
            return d;
        }
        int l=deep(root->left,d+1);
        int r=deep(root->right,d+1);
        if(l>=r)
        {
            return l;
        }
        else{
            return r;
        }
    }
    int maxDepth(TreeNode* root) {
        int i=deep(root,0);
        return i;
    }
};

解法

二叉树大部分用的都是递归,取左+取右,比较

226. 翻转二叉树

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

示例 1:

输入:root = [4,2,7,1,3,6,9] 输出:[4,7,2,9,6,3,1]

示例 2:

输入:root = [2,1,3] 输出:[2,3,1]

示例 3:

输入:root = [] 输出:[]

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(root==nullptr) return nullptr;
        TreeNode *temp=root->left;
        invertTree(root->left);
        invertTree(root->right);   
        root->left=root->right;
        root->right=temp;
        return root;
    }
};

解法

二叉树大部分用的都是递归,左子树先换,右子树再换,最后在根节点换

101. 对称二叉树

给你一个二叉树的根节点 root , 检查它是否轴对称。

示例 1:

输入:root = [1,2,2,3,4,4,3] 输出:true

示例 2:

输入:root = [1,2,2,null,3,null,3] 输出:false

代码

 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
class Solution {
public:
    bool fx(TreeNode* left,TreeNode* right){
        if(left==nullptr&&right==nullptr){
            return true;
        }
        if(left==nullptr&&right!=nullptr){
            return false;
        }
        if(left!=nullptr&&right==nullptr){
            return false;
        }
        if(left->val==right->val){
            int a=fx(left->left,right->right);
            int b=fx(left->right,right->left);
            if(a==true&&b==true){
                return true;
            }
            else{
                return false;
            }
        }
        return false;
    }
    bool isSymmetric(TreeNode* root) {
        return fx(root->left,root->right);
    }
};

解法

二叉树大部分用的都是递归,传入左右两个节点,对称的来比较

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
2777619715@qq.com
使用 Hugo 构建
主题 StackJimmy 设计
代码来自:https://yeelz.com/post/564.html