目录:
模板代码
#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;
}