C++学习笔记

本文汇总了多种编程算法模板,包括闰年和平年判断、数的逆序、模运算规则、快速幂、除法取模、素数筛法等,并探讨了C++中设置最大值、最小值、有效数字和小数点位数的方法。此外,还介绍了如何使用memset、cin/cout加速以及处理字符串输入等问题,涉及了秦九韶算法和进制转换的解题策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

模板代码

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef pair<int,int> pii;

#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define lowbit(x)   ((x)&(-x))
#define fi first
#define se second
#define endl '\n'
#define pb push_back

template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }

template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char c = getchar();
    while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
    while (c <= '9' && c >= '0') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    x *= f;
}

int main()
{
    return 0;
}

闰年与平年条件

#include<stdio.h>
int main()
{
    /*1、能被4整除,但不能别100整除
      2、能被400整除(满足二者之一即为闰年)
      闰年二月份29天
      平年二月份28天
      if条件中两个条件都得写哇
    */
  if((year%4 == 0 && year%100 != 0) ||year%400 == 0);
}

实现一个数的逆序

给定一个整数,请输入这个整数的逆序:
样例输入 #1:
13
样例输出 #1:
31
样例输入 #2:
-52
样例输出 #2:
-25

#include<bits/stdc++.h>
using namespace std;
int reverse(int x) {
	int res = 0, temp = abs(x);
	while (temp) {
		res = res * 10 + temp % 10;
		temp /= 10;
	}
	return x >= 0 ? res : -res;
}
int main() {
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int num,res;
	cin >> num;
	res = reverse(num);
	cout << res << endl;
	return 0;
}

模运算的基本规则

(a + b) % p = (a % p + b % p) % p
(a – b) % p = (a % p – b % p) % p
(a * b) % p = (a % p * b % p) % p
(a b) % p = ((a % p) b) % p

快速幂模板

#include<bits/stdc++.h>
using namespace std;
const int mod = 998244353;
long long fastPow(long long base,long long n)
{
    long long res = 1;
    while (n)
    {
        if(n&1)   res = (res*base)%mod;//二进制最后一位是否为1
        base  = (base*base)%mod;
        n>>=1;//n的二进制右移一位
    }
    return res;
}

此处,保险起见,全使用了long long类型(防爆嘛),主要是这里的mod如果过大的话,就像上面代码里面的,很可能在中间运算时因数据类型不够大而导致爆掉,如果出现这样情况,就很可能需要花费大量时间去找问题(我起初就出现了这样的问题)。在这里插入图片描述

除法取模模板

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll fpow(ll a,ll b){a%=mod;ll ret=1;while(b){if(b&1)ret=ret*a%mod;a=a*a%mod;b>>=1;}return ret;}
int main()
{
    ll m,n;
    cin >> m >> n;//求(m/n)%mod可以转换为(m*x)%mod,其中x为n的逆元
    cout << m*fpow(n,mod-2)%mod;//其中fpow(n,mod-2)为n的逆元
}

素数筛

注:在C++中定义int型全局变量时默认为0,定义bool类型时默认为false即0

试除法

bool is_prime(int n)
{
	if(n<=1)	return false;
	for(int i = 2;i <= n / i;i++)
		if(n%i==0)	return false;
	return true;
}

埃式筛法

#include<bits/stdc++.h>
using namespace std;
const int N = 1e8+10;
int prime[N];
bool book[N];//默认为false
void E_sieve(int n)
{
    for (int i = 2; i*i <= n; i++)
    {
        if(!book[i])//如果是素数,则开始筛选,(素数的倍数都不是素数)
            for (int j = i*i; j <= n; j+=i)//从i*i开始是经过了优化的。
                book[j] = true;//标记为非素数
    }
    int idx = 0;//下标索引
    for (int i = 2; i <= n; i++)
    {//从2开始,没有被筛选过的就都是素数了
        if(!book[i])   prime[++idx] = i;
    }   
}

欧拉筛

const int N = 1e8+10;
int prime[N];
bool book[N];
void O_sieve(int n)
{
    int idx = 0;
    book[0] = book[1] = true;//初始化0和1都为非素数
    for (int i = 2; i <= n; i++)
    {
        if(!book[i])    prime[++idx] = i;
        for (int j = 1; j <= idx && i * prime[j] <= n; j++)
        {
            book[i*prime[j]] = true;
            if(i%prime[j]==0)   break;
        }
    }
}

设定最大值和最小值

#include<bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int imax = INT_MAX;
    int imin = INT_MIN;
    long long lmax = LONG_LONG_MAX;
    long long lmin = LONG_LONG_MIN;
    cout << "INT_MAX的值为:" << imax << endl;
    cout << "INT_MIN的值为:" << imin << endl;
    cout << "LONG_LONG_MAX的值为:" << lmax << endl;
    cout << "LONG_LONG_MIN的值为:" << lmin << endl;
    return 0;
    /*下面是测试的结果:
    INT_MAX的值为:2147483647
    INT_MIN的值为:-2147483648
    LONG_LONG_MAX的值为:9223372036854775807
    LONG_LONG_MIN的值为:-9223372036854775808
    */
}

C++设定有效数字个数、小数点位数

setprecision是一个计算机函数,功能是控制输出流显示浮点数的有效数字个数,如果和fixed合用的话,可以控制小数点后面有几位。
下面是代码测试:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    double pai = 13.1415926;
    cout << setprecision(3) << pai << endl;//设置有效数字个数
    cout << fixed << setprecision(3) << pai<<endl;//设置小数点后位数(含四舍五入)
    int n = 4;
    cout << fixed << setprecision(n) << pai << endl;//可以通过键盘录入来控制小数点位数
    return 0;
}
/*
结果:
13.1
13.142
13.1416
*/

memset的常见用法

#include<bits/stdc++.h>
using namespace std;
int main()
{//在初始化int型数组中的元素时,memset只能正确赋值0和-1
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int a[10];
    memset(a,-1,sizeof a);
    for(auto i : a) cout << i << " ";
    cout << endl;
    int b[10];
    memset(b,0,sizeof b);
    for(auto i : b) cout << i << " ";
    return 0;
}
/*
测试结果:
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 
0 0 0 0 0 0 0 0 0 0 
*/

cin,cout加速模板(附缺点)

ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);

虽说有了加速,cin和cout不会与scanf和printf在时间上相差太多,但如果数据很大的话,即使加速,也无济于事了,如图:
这是加速的cin,cout在后面需要输出较多数据时的结果:
在这里插入图片描述
这是直接scanf和printf下的结果:
在这里插入图片描述
基本上前三个数据时间相差无几,但数据越大,时间差距也越大!
综上所述,当需要输入输出数据量较多时,用scanf和printf更合适!

使用回车键结束while(cin>>)循环输入

利用get函数,测例如下:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    char c;
    string str;
    while ((cin >> str).get(c))
    {
        if(c=='\n') break;
    }
    cout << "最后一个字符串:" << str;
    return 0;
}

string读入空格的方法

#include<bits/stdc++.h>
using namespace std;
int main()
{
    string s;
    getline(cin,s);
    cout << s;
}
/*
输入:
ab c
输出:
ab c
*/

超过两个数的比较大小(个数适中)

在min或max函数中使用大括号{}即可实现,下面是测试代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    cout << min({1,2,3,5})<<endl;
    cout << max({1,2,3,5})<<endl;
}
/*
结果:
1
5
*/

sort与priority_queue的某方面的不同

sort:默认是从小到大排序,如果加上greater,则变成从大到小排序。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    vector<int> v;
    v.push_back(1);
    v.push_back(3);
    v.push_back(2);
    sort(v.begin(),v.end(),greater<int>());//从大到小排
    sort(v.begin(),v.end(),less<int>());//从小到大排
    for(auto i : v)
    {
        cout << i <<" ";
    }
}

priority_queue:默认是大根堆,加上greater后会变成小根堆。
定义:priority_queue<Type, Container, Functional>
Type 就是数据类型,Container 就是容器类型(Container是某些容器,如vector等),Functional 就是比较的方式。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    //升序队列,小根堆
    priority_queue<int,vector<int>,greater<int>> q1;
    //降序队列,大根堆
    priority_queue<int,vector<int>,less<int>> q2;
    return 0;
}

lower_bound与upper_bound

若数组升序排列
lower_bound(begin, end, a) 返回数组[begin, end)之间第一个大于或等于a的地址,找不到返回end
upper_bound(begin, end, a) 返回数组[begin, end)之间第一个大于a的地址,找不到返回end

若数组降序排列,可写上比较函数greater()
lower_bound(begin, end, a, greater()) 返回数组[begin, end)之间第一个小于或等于a的地址,找不到返回end
upper_bound(begin, end, a, greater()) 返回数组[begin, end)之间第一个小于a的地址,找不到返回end

如需返回下标索引,直接减去数组首地址即可。

测试如下:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
#define rep(i,l,r)  for(int i=(l);i<=(r);i++)
#define per(i,r,l)  for(int i=(r);i>=(l);i--)
const ll mod = 999068070;

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int a[] = {1,2,34};
    cout << lower_bound(a,a + 3,2) - a;
}
/*
结果:
1
*/

模数定义

如果模数是定值,将其定义为常量比定义为全局变量速度更快,某些情况下都能决定是否出现TLE了(我就出现过一次这种情况!)。

秦九韶算法

笨拙的手指

题目链接
题目描述
奶牛贝茜正在学习如何在不同进制之间转换数字。

但是她总是犯错误,因为她无法轻易的用两个前蹄握住笔。

每当贝茜将数字转换为一个新的进制并写下结果时,她总是将其中的某一位数字写错。

例如,如果她将数字 14 转换为二进制数,那么正确的结果应为 1110,但她可能会写下 0110 或 1111。

贝茜不会额外添加或删除数字,但是可能会由于写错数字的原因,写下包含前导 0 的数字。

给定贝茜将数字 N 转换为二进制数字以及三进制数字的结果,请确定 N 的正确初始值(十进制表示)。

输入格式
第一行包含 N 的二进制表示,其中一位是错误的。

第二行包含 N 的三进制表示,其中一位是错误的。

输出格式
输出正确的 N 的值。

数据范围
0 ≤ N ≤ 109,且存在唯一解。

输入样例:
1010
212
输出样例:
14
样例解释
14 在二进制下的正确表示为 1110,在三进制下的正确表示为 112。

思路:
二进制和三进制中各是只有一个位置出现问题,我们可以先枚举二进制中的每一个对立的位(如果是0将其变成1,如果是1将其变成0),将改变后的每一个数都存在哈希表中,接着去枚举三进制的每一位的情况(这样复杂度很低,效率非常高),如果修改位后其在哈希表中出现过,即为正解。求解过程中,我们用到了将二进制、三进制转换为十进制的算法:秦九韶算法

#include<iostream>
#include<cstring>
#include<algorithm>
#include<unordered_set>

using namespace std;

int get(string s,int b)//将b进制的s转成十进制
{
    int res = 0;
    //秦九韶算法
    for(auto c : s)
        res = res * b + c - '0';
    return res;
}

int main()
{
    string a,b;
    cin >> a >> b;
    unordered_set<int> S;
    for(auto& c : a)
    {//0的ASCII码是48,1的ASCII码是49,我们需要把48与49对调,异或一个1即可
        c ^= 1;
        S.insert(get(a,2));
        c ^= 1;
    }
    for(auto& c : b)
    {
        char t = c;
        for(int i = 0;i < 3;i++)
            if(i + '0' != t)//i加'0'的意思是将这个数字转换为ASCII码
            {
                c = i + '0';
                int x = get(b,3);
                if(S.count(x))//如果同时在集合中出现的话,即为解
                {
                    cout << x << endl;
                    return 0;
                }
            }
        c = t;//如果没有出现过的话需要将c恢复成原状
    }
    return 0;
}

X进制减法

题目链接
题目描述
进制规定了数字在数位上逢几进一。

X 进制是一种很神奇的进制,因为其每一数位的进制并不固定!

例如说某种 X 进制数,最低数位为二进制,第二数位为十进制,第三数位为八进制,则 X 进制数 321 转换为十进制数为 65。

现在有两个 X 进制表示的整数 A 和 B,但是其具体每一数位的进制还不确定,只知道 A 和 B 是同一进制规则,且每一数位最高为 N 进制,最低为二进制。

请你算出 A−B 的结果最小可能是多少。

请注意,你需要保证 A 和 B 在 X 进制下都是合法的,即每一数位上的数字要小于其进制。

输入格式
第一行一个正整数 N,含义如题面所述。

第二行一个正整数 Ma,表示 X 进制数 A 的位数。

第三行 Ma 个用空格分开的整数,表示 X 进制数 A 按从高位到低位顺序各个数位上的数字在十进制下的表示。

第四行一个正整数 Mb,表示 X 进制数 B 的位数。

第五行 Mb 个用空格分开的整数,表示 X 进制数 B 按从高位到低位顺序各个数位上的数字在十进制下的表示。

请注意,输入中的所有数字都是十进制的。

输出格式
输出一行一个整数,表示 X 进制数 A−B 的结果的最小可能值转换为十进制后再模 1000000007 的结果。

数据范围
对于 30% 的数据,N ≤ 10;Ma,Mb ≤ 8,
对于 100% 的数据,2 ≤ N ≤ 1000;1 ≤ Ma,Mb ≤ 100000; A ≥ B。

输入样例:
11
3
10 4 0
3
1 2 0
输出样例:
94
样例解释
当进制为:最低位 2 进制,第二数位 5 进制,第三数位 11 进制时,减法得到的差最小。

此时 A 在十进制下是 108,B 在十进制下是 14,差值是 94。

思路:
将这两个数的个位对齐,即从高位开始存储,之后使用秦九韶算法进行权值的计算,可以将朴素的O(n2)的暴力转为O(n)。

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;

const int N = 1e5+10,mod = 1e9+7;

int n,m1,m,m2;
int a[N],b[N];

int main()
{
    scanf("%d",&n);
    scanf("%d",&m1);//读入的时候有可能这两个数的位数不一样,我们是个位对齐,而不是高位对齐(因为高位对不齐)
    for(int i = m1 - 1;i >= 0;i--)  scanf("%d",&a[i]);//故从高往低存储
    scanf("%d",&m2);
    for(int i = m2 - 1;i >= 0;i--)  scanf("%d",&b[i]);

    int m = max(m1,m2);//取较大的位数

    int res = 0;
    for(int i = m - 1;i >= 0;i--)//秦九韶算法
        res = (res * (ll)max({2,a[i] + 1,b[i] + 1}) + a[i] - b[i]) % mod;//最低得是二进制
    printf("%d",res);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值