目录
A | 算法提高 找素数 |
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
给定区间[L, R] , 请计算区间中素数的个数。
输入格式
两个数L和R。
输出格式
一行,区间中素数的个数。
样例输入
2 11
样例输出
5
数据规模和约定
2 <= L <= R <= 2147483647 R-L <= 1000000
思路:数据范围过大.普通的素数打表肯定是不行的,注意使用R-L <= 1000000这个条件,对sqrt(R)以内的素数进行打表,筛出L到R中素数的倍数(即合数),保存和数-L的状态,遍历得到结果.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
typedef long long ll;
using namespace std;
const int N = 1000000 + 10;
int prime[N];
int num[N];
int ans[N];
int cnt=0;
void prim(int n)
{
for(int i=2; i<=n; i++)
{
if(!prime[i])
{
num[cnt++] = i;
for(int j=i*2; j<=n; j+=i)
{
prime[j] = 1;
}
}
}
}
int main()
{
int l,r;
cin>>l>>r;
prim((int)sqrt(r));
for(int i=0; i<cnt; i++)
{
int a = num[i];
for(ll j=(l+a-1)/a*a; j<=r; j+=a)//注意减1,否则容易漏掉l-l+a中a的倍数.
{
if(j == a) continue;
else ans[j-l] = 1;
}
}
int cnt = 0;
for(int i=0; i<=r-l; i++)
{
if(ans[i] == 0) cnt++;
}
cout<<cnt;
return 0;
}
B | 算法训练 区间k大数查询 |
时间限制:1.0s 内存限制:256.0MB
问题描述
给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个。
输入格式
第一行包含一个数n,表示序列长度。
第二行包含n个正整数,表示给定的序列。
第三个包含一个正整数m,表示询问个数。
接下来m行,每行三个数l,r,K,表示询问序列从左往右第l个数到第r个数中,从大往小第K大的数是哪个。序列元素从1开始标号。
输出格式
总共输出m行,每行一个数,表示询问的答案。
样例输入
5
1 2 3 4 5
2
1 5 2
2 3 2
样例输出
4
2
数据规模与约定
对于30%的数据,n,m<=100;
对于100%的数据,n,m<=1000;
保证k<=(r-l+1),序列中的数<=106。
思路:把原数组L-R复制到处理数组中,从大到小进行排序,输出第K个.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
typedef long long ll;
using namespace std;
const int N = 1000000 + 10;
int ans;
int num[N];
int temp[N];
bool cmp(int a, int b)
{
return a > b;
}
int main()
{
int n,m;
cin>>n;
for(int i=1; i<=n; i++)
scanf("%d",&num[i]);
cin>>m;
int l,r,k;
while(m--)
{
scanf("%d %d %d",&l,&r,&k);
int cnt=1;
for(int i=l; i<=r; i++)
temp[cnt++] = num[i];
sort(temp + 1, temp + cnt, cmp);
printf("%d\n",temp[k]);
}
return 0;
}
C | 算法训练 操作格子 |
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
有n个格子,从左到右放成一排,编号为1-n。
共有m次操作,有3种操作类型:
1.修改一个格子的权值,
2.求连续一段格子权值和,
3.求连续一段格子的最大值。
对于每个2、3操作输出你所求出的结果。
输入格式
第一行2个整数n,m。
接下来一行n个整数表示n个格子的初始权值。
接下来m行,每行3个整数p,x,y,p表示操作类型,p=1时表示修改格子x的权值为y,p=2时表示求区间[x,y]内格子权值和,p=3时表示求区间[x,y]内格子最大的权值。
输出格式
有若干行,行数等于p=2或3的操作总数。
每行1个整数,对应了每个p=2或3操作的结果。
样例输入
4 3
1 2 3 4
2 1 3
1 4 3
3 1 4
样例输出
6
3
数据规模与约定
对于20%的数据n <= 100,m <= 200。
对于50%的数据n <= 5000,m <= 5000。
对于100%的数据1 <= n <= 100000,m <= 100000,0 <= 格子权值 <= 10000。
思路:线段树模板题,需要同时维护一个maxn值和一个sum值.
#include<iostream>
#include<climits>
#define ll long long
using namespace std;
const int N = 1e5 + 10;
int n,m;
struct node{
int l,r;
int maxn;
int sum;
}tree[4*N];
int num[N];
void build(int u,int l,int r)
{
if(l == r)
{
tree[u] = {l,r,num[r],num[r]};
}
else
{
int mid = (r + l) >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
tree[u] = {l,r};
tree[u].maxn = max(tree[u << 1].maxn, tree[u << 1 | 1].maxn);
tree[u].sum = tree[u << 1].sum + tree[u << 1 | 1].sum;
}
}
int query1(int u, int l ,int r)
{
if(tree[u].l >= l && tree[u].r <= r) return tree[u].maxn;
int maxnn = INT_MIN;
int mid = (tree[u].l + tree[u].r) >> 1;
if(mid >= l) maxnn = query1(u << 1, l, r);
if(mid < r) maxnn = max(maxnn, query1(u << 1 | 1, l, r));
return maxnn;
}
int query2(int u, int l ,int r)
{
if(tree[u].l >= l && tree[u].r <= r) return tree[u].sum;
int sum = 0;
int mid = (tree[u].l + tree[u].r) >> 1;
if(mid >= l) sum = query2(u << 1, l, r);
if(mid < r) sum += query2(u << 1 | 1, l, r);
return sum;
}
void modify(int u, int x, int v)
{
if(tree[u].l == tree[u].r)
{
tree[u].maxn = v;
tree[u].sum = v;
}
else
{
int mid =(tree[u].l + tree[u].r) >> 1;
if(x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1,x,v);
tree[u].sum = tree[u << 1].sum + tree[u << 1 | 1].sum;
tree[u].maxn = max(tree[u << 1].maxn, tree[u << 1 | 1].maxn);
}
}
int main()
{
cin>>n>>m;
for(int i=1; i<=n; i++)
{
scanf("%d",&num[i]);
}
build(1,1,n);
for(int i=0; i<m; i++)
{
int opt;
cin>>opt;
if(opt == 1)
{
int x, y;
cin>>x>>y;
modify(1,x,y);
}
else if(opt == 2)
{
int l,r;
cin>>l>>r;
printf("%lld\n",query2(1,l,r));
}
else if(opt == 3)
{
int l,r;
cin>>l>>r;
cout<<query1(1,l,r)<<endl;
}
}
return 0;
}
D | 算法提高 士兵排队问题 |
资源限制
时间限制:1.0s 内存限制:256.0MB
试题
有N个士兵(1≤N≤26),编号依次为A,B,C,…,队列训练时,指挥官要把一些士兵从高到矮一次排成一行,但现在指挥官不能直接获得每个人的身高信息,只能获得“P1比P2高”这样的比较结果(P1、P2∈A,B,C,…,Z,记为 P1>P2),如”A>B”表示A比B高。
请编一程序,根据所得到的比较结果求出一种符合条件的排队方案。
(注:比较结果中没有涉及的士兵不参加排队)
输入要求
比较结果从文本文件中读入(文件由键盘输入),每个比较结果在文本文件中占一行。
输出要求
若输入数据无解,打印“No Answer!”信息,否则从高到矮一次输出每一个士兵的编号,中间无分割符,并把结果写入文本文件中,文件由键盘输入:
样例输入
A>B
B>D
F>D
样例输出
AFBD
思路:拓扑排序模板题,判断有没有环,无环即可.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
typedef long long ll;
using namespace std;
const int N = 300;
int in[N];
vector <char> v[N];
vector <char> ans;
bool st[N];
int main()
{
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
char c[4];
while(~scanf("%s",c+1))
{
char x = c[1];
char y = c[3];
v[x].push_back(y);
in[y]++;
st[x]=st[y]=1;
}
queue<char> q;
int total = 0;
for(int i='A'; i<='Z'; i++)
{
if(in[i] == 0 && st[i] == 1)
{
q.push(i);
}
if(st[i] == 1) total++;
}
while(!q.empty())
{
char tem = q.front();
total--;
ans.push_back(tem);
q.pop();
for(int i=0; i<v[tem].size(); i++)
{
in[v[tem][i]]--;
if(in[v[tem][i]] == 0) q.push(v[tem][i]);
}
}
if(total == 0)
{
for(int i=0; i<ans.size(); i++)
cout<<ans[i];
}
else puts("No Answer!");
return 0;
}
E | 基础练习 十六进制转八进制 |
资源限制
时间限制:1.0s 内存限制:512.0MB
问题描述
给定n个十六进制正整数,输出它们对应的八进制数。
输入格式
输入的第一行为一个正整数n (1<=n<=10)。
接下来n行,每行一个由0~9、大写字母A~F组成的字符串,表示要转换的十六进制正整数,每个十六进制数长度不超过100000。
输出格式
输出n行,每行为输入对应的八进制正整数。
【注意】
输入的十六进制数不会有前导0,比如012A。
输出的八进制数也不能有前导0。
样例输入
2
39
123ABC
样例输出
71
4435274
【提示】
先将十六进制数转换成某进制数,再由某进制数转换成八进制。
思路:利用sscanf的16进制读取功能每六位读取一次,再利用printf的8进制的输出功能输出,注意对不足六位的16进制数及转化后不足八位的16进制数进行特殊处理.
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 100000 + 10;
char num[N];
int main()
{
int n;
cin>>n;
for(int i=0; i<n; i++)
{
scanf("%s",num);
int len = strlen(num);
int x;
if(len % 6)//如果16进制数不能刚好分成多个8位8进制数,则在开头对多出来的部分特殊处理
{
switch(len % 6)
{
case 1:
sscanf(num,"%1x",&x);
printf("%o",x);
break;//别忘了break
case 2:
sscanf(num,"%2x",&x);
printf("%o",x);
break;
case 3:
sscanf(num,"%3x",&x);
printf("%o",x);
break;
case 4:
sscanf(num,"%4x",&x);
printf("%o",x);
break;
case 5:
sscanf(num,"%5x",&x);
printf("%o",x);
break;
}
}
int j = len % 6;
while(j < len)
{
sscanf(num+j,"%6x",&x);
if(j==0) printf("%o",x);
else printf("%08o",x); //在16进制数的开头之后可能有6位数太小,转化成8进制数不足8位,需要在不足的位置添0
j+=6;
}
puts("");
}
return 0;
}
F | 算法训练 最大最小公倍数 |
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
已知一个正整数N,问从1~N中任选出三个数,他们的最小公倍数最大可以为多少。
输入格式
输入一个正整数N。
输出格式
输出一个整数,表示你找到的最小公倍数。
样例输入
9
样例输出
504
数据规模与约定
1 <= N <= 106。
思路:这题的关键在与只选取3个数,我们知道如果只选择两个数的话,那么肯定选两个相邻的两数,因为他两互质所以最小公倍数是最大的。所以面对要选择三个数,我们也可以设法选出3个互质的数。对于相邻的a,b,c三个数有奇偶奇,偶奇偶,可以发现奇偶奇三个数一定是互质的,a与b互质,b与c互质,a与b之间不可有共因子2,|c-a|<3,所以也不可能存在共因子3,之后的更别说了。
然后是偶奇偶,其最大最小公倍数必定至少除2,所以可以尝试把其中一个偶换成奇数,且不构成3的倍数,或者整体-1,变成奇偶奇。
#include <iostream>
#include <algorithm>
#include <map>
#include <string>
#include <queue>
#include <vector>
using namespace std;
const int maxn = 300;
typedef pair<int,int> pii;
typedef long long ll;
ll N;
int main(){
cin>>N;
ll mx = 0;
if(N<3) mx = N;
else if(N&1){ //奇偶奇
mx = N*(N-1)*(N-2);
}else{
if(N%3 !=0){ //N和N-3不是3的倍数
mx = N*(N-1)*(N-3);
}else{ //整体-1
mx = (N-1)*(N-2)*(N-3);
}
}
cout<<mx<<endl;
return 0;
}
G | 算法提高 欧拉函数 |
资源限制
时间限制:1.0s 内存限制:512.0MB
说明
2016.4.5 已更新试题,请重新提交自己的程序。
问题描述
给定一个大于1,不超过2000000的正整数n,输出欧拉函数,phi(n)的值。
如果你并不了解欧拉函数,那么请参阅提示。
输入格式
在给定的输入文件中进行读入:
一行一个正整数n。
输出格式
将输出信息输出到指定的文件中:
一行一个整数表示phi(n)。
样例输入
17
样例输出
16
提示
欧拉函数phi(n)是数论中非常重要的一个函数,其表示1到n-1之间,与n互质的数的个数。显然的,我们可以通过定义直接计算phi(n)。
当然,phi(n)还有这么一种计算方法。
首先我们对n进行质因数分解,不妨设n=p1^a1 * p2^a2 * ... * pk^ak (这里a^b表示a的b次幂,p1到pk为k个互不相同的质数,a1到ak均为正整数),那么
phi(n)=n(1-(1/p1))(1-(1/p2))....(1-(1/pk))
稍稍化简一下就是
phi(n)=n(p1-1)(p2-1)...(pk-1)/(p1*p2*...*pk)
计算的时候小心中间计算结果超过int类型上界,可通过调整公式各项的计算顺序避免(比如先做除法)!
用到的算法:欧拉筛
线性复杂度,把前maxn个数的欧拉值筛选出来
#include <iostream>
#include <algorithm>
#include <map>
#include <string>
#include <queue>
#include <vector>
using namespace std;
const int maxn = 2e6+10;
typedef pair<int,int> pii;
typedef long long ll;
ll N;
int E[maxn];
void init()
{
E[1] = 1;
for(int i=2;i<maxn;i++){
if(!E[i])
for(int j=i;j<maxn;j+=i){
if(!E[j]) E[j]=j;
E[j] = E[j]/i*(i-1);
}
}
}
int main(){
init();
cin>>N;
cout<<E[N]<<endl;
return 0;
}
H | 算法训练 乘法次数 |
资源限制
时间限制:1.0s 内存限制:999.4MB
问题描述
给你一个非零整数,让你求这个数的n次方,每次相乘的结果可以在后面使用,求至少需要多少次乘。如24:2*2=22(第一次乘),22*22=24(第二次乘),所以最少共2次;
输入格式
第一行m表示有m(1<=m<=100)组测试数据;
每一组测试数据有一整数n(0<n<=100000000);
输出格式
输出每组测试数据所需次数s;
样例输入
3
2
3
4
样例输出
1
2
2
用到的算法:快速幂
思路:明显可以看出,求出n次幂的最快方法应该是不断累乘,即2^4 * 2^4,2^8 * 2^8,尽快求出n的二进制最高位的2^poww,再乘以已经算出过的低位即可.
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
int T;
ll ksm(ll a,ll b){
int poww = 0,cnt = 0;
while(b){
if(b&1) {
cnt += 1;
}
a = a*a% mod;// 也可以不写
poww++;
b>>=1;
}
return poww+cnt-2;
}
int main(){
cin>>T;
while(T--){
ll b;scanf("%lld",&b);
printf("%lld\n",ksm(2,b));
}
return 0;
}
I | 算法训练 方格取数 |
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
设有N*N的方格图(N<=10),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。
某人从图的左上角的A 点(1,1)出发,可以向下行走,也可以向右走,直到到达右下角的B点(N,N)。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
此人从A点到B 点共走两次,试找出2条这样的路径,使得取得的数之和为最大。
输入格式
输入的第一行为一个整数N(表示N*N的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的0表示输入结束。
输出格式
只需输出一个整数,表示2条路径上取得的最大的和。
样例输入
8
2 3 13
2 6 6
3 5 7
4 4 14
5 2 21
5 6 4
6 3 15
7 2 14
0 0 0
样例输出
67
思路:四维DP,两个人同时走.
#include<iostream>
#include<map>
#include<cmath>
using namespace std;
const int N = 11;
int val[N][N];
int dp[N][N][N][N];
int main()
{
int n;
cin>>n;
int x,y,z;
while(cin>>x>>y>>z&&x&&y&&z)
{
val[x][y] = z;
}
dp[1][1][1][1] = val[1][1];
for(int x1=1; x1<=n; x1++)
{
for(int y1=1; y1<=n; y1++)
{
for(int x2=1; x2<=n; x2++)
{
for(int y2=1; y2<=n; y2++)
{
if(x1+y1 != x2+y2) continue;
int tem = 0;
tem = max(dp[x1-1][y1][x2-1][y2],dp[x1-1][y1][x2][y2-1]);
tem = max(dp[x1][y1-1][x2-1][y2], tem);
tem = max(dp[x1][y1-1][x2][y2-1], tem);
dp[x1][y1][x2][y2] = tem + val[x1][y1] + val[x2][y2];
if(x1 == x2 && y1 == y2) dp[x1][y1][x2][y2] -=val[x1][y1];
}
}
}
}
cout<<dp[n][n][n][n];
return 0;
}
//8
//2 3 13
//2 6 6
//3 5 7
//4 4 14
//5 2 21
//5 6 4
//6 3 15
//7 2 14
//0 0 0
时间限制:1.0s 内存限制:256.0MB资源限制
问题描述
对于长度为5位的一个01串,每一位都可能是0或1,一共有32种可能。它们的前几个是:
00000
00001
00010
00011
00100
请按从小到大的顺序输出这32种01串。
输入格式
本试题没有输入。
输出格式
输出32行,按从小到大的顺序每行一个长度为5的01串。
样例输出
00000
00001
00010
00011
<以下部分省略>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
typedef long long ll;
using namespace std;
int num[100];
int main()
{
for(int i=0; i<32; i++)
{
int cnt=0;
int j=i;
while(j)
{
num[cnt++] = j % 2;
j = j/2;
}
for(int k=4; k>=0; k--)
cout<<num[k];
puts("");
}
}
部分题解代码来自:https://blog.nowcoder.net/n/09f531a212f54db98f1aceee72468bfb?tdsourcetag=s_pctim_aiomsg