必须说明的是set关联式容器。set作为一个容器也是用来存储同一数据类型的数据类型,并且能从一个数据集合中取出数据,在set中每个元素的值都唯一,而且系统能根据元素的值自动进行排序。应该注意的是set中数元素的值不能直接被改变。
begin() ,返回set容器的第一个元素
end() ,返回set容器的最后一个元素
clear() ,删除set容器中的所有的元素
empty() ,判断set容器是否为空
max_size() ,返回set容器可能包含的元素最大个数
size() ,返回当前set容器中的元素个数
rbegin ,返回的值和end()相同
rend() ,返回的值和rbegin()相同
满二叉树
如果二叉树中除了叶子结点,每个结点的度都为 2,则此二叉树称为满二叉树。
满二叉树示意图
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=image.png&pos_id=img-Lkp8Brpm-1698454585300
图 2 满二叉树示意图
如图 2 所示就是一棵满二叉树。
满二叉树除了满足普通二叉树的性质,还具有以下性质:
满二叉树中第 i 层的节点数为 2n-1 个。
深度为 k 的满二叉树必有 2k-1 个节点 ,叶子数为 2k-1。
满二叉树中不存在度为 1 的节点,每一个分支点中都两棵深度相同的子树,且叶子节点都在最底层。
具有 n 个节点的满二叉树的深度为 log2(n+1)。
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=image-1.png&pos_id=img-RouL2xbF-1698454585302
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=image-1.png&pos_id=img-6QfvPjLd-1698454585303
pai运算
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=image-2.png&pos_id=img-vFpAI9bH-1698454585303
##E题 exgcd
https://www.cnblogs.com/juruo-zzt/p/exgcd.html
void solve()
{
ll n, m, x;
cin >> n >> m >> x;
if(!x)
{
cout << 0 << '\n';
return;
}
x = n - x;
vector<ll> a(m + 1);
for(int i = 1; i <= m; i++) cin >> a[i];
ll y = 0;
for(int i = 1; i <= m; i++) y = (y + a[i]) % n;
ll s = 0;
ll ans = 1e18;
for(int i = 1; i <= m; i++)
{
s = (s + a[i]) % n; //逐步sum
ll z = (x - s) % n; //原始值减去s
z = (z % n + n) % n;
ll t1, t2;
ll d = exgcd(y, n, t1, t2); //sum 和 要求n的最大公约数 // 如果为 n
// cout << y << ' ' << n << ' ' << t1 << ' ' << t2 << '\n';
d = abs(d);
if(z % d == 0)
{
t1 *= z / d;
ll t = n / d;
t1 = (t1 % t + t) % t;
ans = min(ans, t1 * m + i);
}
}
if(ans == 1e18) ans = -1;
cout << ans << '\n';
}
exgcd
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=image-3.png&pos_id=img-x49ByRpT-1698454585304
gcd求最大公约数
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if (b == 0)
{
x = 1;
y = 0;
return a;
}
ll d = exgcd(b, a % b, y, x);
y -= (a / b) *x;
return d;
}
https://blog.csdn.net/qq_49593247/article/details/125588288 !!!例题
//代码实现
1ll<<60 表示将数字 1 左移 60 位。在 C++ 中,后缀 ll 表示将数字字面量解释为 long long 类型,而 << 运算符表示左移操作。这个表达式的结果是一个非常大的整数,即 2 的 60 次方。
0x3f3f3f3f 是一个十六进制常量,表示一个特定的整数值。该值在很多算法和竞赛中经常用于初始化答案或作为特殊标志。这个数值在二进制中表示为 0011 1111 0011 1111 0011 1111 0011 1111,为一个十进制数值 1061109567。
这两个常量的区别:
1ll<<60 表示一个非常大的整数,通常用于表示一个超出常规范围的计数或计算结果。
0x3f3f3f3f 是一个固定的整数常量,通常用于初始化答案或标记特殊值。在某些上下文中,它被用作初始最小值或无效值。
符号 ~ 是取反运算符(bitwise not operator)。
在这种情况下,~ret 是对 ret 进行按位取反的操作。如果 ret 的值为 -1,则取反后得到 0,否则得到一个非零的值。通过 if(~ret) 来检查 ret 是否为 -1 的目的是判断 solve 函数是否返回了有效的解。
在 C++ 中,~ 运算符将整数的每个位取反,包括符号位。因此,当 ret 的值为 -1 时,取反后的结果为 0。而其他任何非零整数都会被取反为一个非零的负数。
https://blog.csdn.net/fx677588/article/details/70767446 哈夫曼编码
floyd 算法 最短路径
https://blog.csdn.net/qq_35644234/article/details/60875818
两个矩阵 一个记录前继 一个记录路径长度
Floyd算法是如何工作的: 1.每次都会选择一个中介点,然后,遍历整个矩阵,查找需要更新的值 即 a[i][k] + a[k][j] < a[i][j]
int dist[VertexMax][VertexMax];
int path[VertexMax][VertexMax];
void ShortestPath_Floyd(MGraph G)
{
int i, j, k;
// 初始化部分
for (i = 0; i < G.vexnum; i++)
{
for (j = 0; j < G.vexnum; j++)
{
dist[i][j] = G.AdjMatrix[i][j];
if (dist[i][j] != MaxInt)
{
path[i][j] = j; // 存入后继
}
else
path[i][j] = -1;
}
}
// 算法核心部分
for (k = 0; k < G.vexnum; k++) // 拿出每个顶点作为遍历条件
for (i = 0; i < G.vexnum; i++)
for (j = 0; j < G.vexnum; j++)
{
if (dist[i][j] > dist[i][k] + dist[k][j])
{
dist[i][j] = dist[i][k] + dist[k][j];
path[i][j] = path[i][k]; // 存入后继 //分两段 要标记中间k
}
}
}
找中间点 每一个路径都便利
void dijsta()
{
for (i = 1; i < n; i++)
{
dis[i] = a[0][i];
if (a[0][i] != INF)
p[i] = 0;
else
p[i] = -1; // 没有前的条件
}
// dijkstra 核心在于每一次在dis里面找最小的, 以这个节点来更新
for (i = 1; i < n; i++)
{
int temp = INF, t = u;
for (k = 1; k < n; k++)
{
if (dis[k] < temp && !flag[k]) // 不止要满足这个dis最小,同时看他还在不在节点里了
{
temp = dis[k];
t = k;
}
}
if (t == u)
retrun; // 找不到更小的了
for (j = 1; j < n; j++)
{
if (dis[j] > dis[t] + dis[t][j] && !flag[j])
{
dis[j] = dis[t] + dis[t][j];
p[j] = t;
}
}
}
维护从一点到另一点
7-3 城市间紧急救援
路径条数 根据最短路径 到每一点的最短路径条数 dod数组依次叠加, 长度相同的 叠加 更小的直接覆盖
#include <iostream>
#include <algorithm>
#include <stack>
#define INF 1e9
using namespace std;
int visited[550], num[550], a[550][550], p[550], dis[550], mon[550], dod[550];
int N, M, S, D;
stack<int> st;
int main()
{
void Dijska();
int i, j, x, y, ss;
cin >> N >> M >> S >> D;
for (i = 0; i < N; i++)
cin >> num[i];
for (i = 0; i < N;i++)
{
for (j = 0; j < N; j++)
a[i][j] = INF;
}
for (i = 0; i < M; i++)
{
cin >> x >> y >> ss;
a[x][y] = ss;
a[y][x] = ss;
}
Dijska();
i = D;
st.push(i);
while(p[i] != -1)
{
st.push(p[i]);
i = p[i];
}
cout << dod[D] << ' '<< mon[D] << endl;
int flag = 0;
while(!st.empty())
{
if(!flag)
{
cout << st.top();
flag = 1;
}
else
cout << ' ' << st.top();
st.pop();
}
return 0;
}
void Dijska()
{
int i, j, c, t;
for (i = 0; i < N; i++)
{
visited[i] = 0;
mon[i] = 0;
dis[i] = a[S][i];
if (dis[i] == INF)
p[i] = -1;
else
{
dod[i] = 1; // 条数
p[i] = S;
if(i != S)
mon[i] = num[S] + num[i];
else
mon[i] = num[i];
}
}
visited[S] = 1;
dis[S] = 0;
for (i = 0; i < N; i++)
{
int temp = INF;
c = S;
for (t = 0; t < N; t++)
{
if (temp > dis[t] && !visited[t])
{
c = t;
temp = dis[t];
}
}
if (c == S)
{
return;
}
visited[c] = 1;
for (j = 0; j < N; j++)
{
if (dis[j] > dis[c] + a[c][j] && !visited[j])
{
dis[j] = dis[c] + a[c][j];
p[j] = c;
dod[j] = dod[c];
mon[j] = mon[c] + num[j];
}
else if(dis[j] == dis[c] + a[c][j] && !visited[j])
{
dod[j] += dod[c]; //路径长度相同 添加当前dod
if (mon[j] < mon[c] + num[j])
{
mon[j] = mon[c] + num[j];
p[j] = c;
}
}
}
}
}
pta 公路村村通
pair
使用带有参数的构造函数初始化:
cpp
std::pair<int, std::string> myPair(10, “Hello”); // 使用整数10和字符串"Hello"进行初始化
使用make_pair函数进行初始化:
cpp
std::pair<int, std::string> myPair = std::make_pair(20, “World”); // 使用整数20和字符串"World"进行初始化
或者使用C++17中的类模板参数推导功能简化为:
cpp
auto myPair = std::make_pair(20, “World”);
直接使用花括号进行列表初始化:
cpp
std::pair<int, std::string> myPair = {30, “Foo”}; // 使用整数30和字符串"Foo"进行初始化
或者使用C++17中的类模板参数推导功能简化为:
cpp
auto myPair = std::pair(30, “Foo”);
调用 ceil(x) 函数来对变量 x 进行向上取整操作。
下面是一个示例代码:
向上取整
#include <iostream>
#include <cmath>
int main() {
double x = 2.3;
double result = ceil(x);
std::cout << "向上取整后的结果为: " << result << std::endl;
return 0;
}
ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
一.sync_with_stdio
这个函数是一个“是否兼容stdio”的开关,C++为了兼容C,保证程序在使用了std::printf和std::cout的时候不发生混乱,将输出流绑到了一起。
cin,cout之所以效率低,是因为先把要输出的东西存入缓冲区,再输出,导致效率降低,而这段语句可以来打消iostream的输入输出缓存,可以节省许多时间,使效率与scanf与printf相差无几.
应用
在ACM里,经常出现数据集超大造成 cin 运行超时 /Time Limit Exceeded【TLE】的情况。这时候大部分人(包括原来我也是)认为这是cin的效率不及scanf的错,甚至还上升到C语言和C++语言的执行效率层面的无聊争论。其实像上文所说,这只是C++为了兼容而采取的保守措施。我们可以在IO之前将stdio解除绑定,这样做了之后要注意不要同时混用cout/cin和printf/scanf之类。
tie是将两个stream绑定的函数,空参数的话返回当前的输出流指针。
在默认的情况下cin绑定的是cout,每次执行 << 操作符的时候都要调用flush,这样会增加IO负担。可以通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。
https://blog.csdn.net/qq_45475271/article/details/107675845
例如,用 system(“color 0A”); 其中color后面的0是背景色代号,A是前景色代号。各颜色代码如下:
0=黑色 1=蓝色 2=绿色 3=湖蓝色 4=红色 5=紫色 6=黄色 7=白色 8=灰色 9=淡蓝色 A=淡绿色 B=淡浅绿色 C=淡红色 D=淡紫色 E=淡黄色 F=亮白色
strstr
char* strstr(const char* haystack, const char* needle);
它的功能是在 haystack 字符串中寻找第一次出现 needle 字符串的位置,并返回该位置的指针。如果没有找到,就返回 NULL。
unique()是C++标准库函数里面的函数,其功能是去除相邻的重复元素(注意一定是相邻元素,且只保留一个),所以使用前需要对数组进行排序
iterator My_Unique (iterator first, iterator last)//两个参数都是迭代器
{
if (first==last)
return last; // 只有一个元素的情况
iterator result = first; // 从第一个元素开始
while (++first != last) // 直到最后一个元素
{
if (*result != *first) // 找到了一个不重复的元素
*(++result)=*first; // 覆盖前面的元素
}
return ++result; // 返回最后不重复元素的下一个元素的坐标的迭代器
}
长度为L的数组a,unique(a,a+n) - a返回的是去重后的数组长度k
erase
(1)erase(pos,n); 删除从pos开始的n个字符,比如erase(0,1)就是删除第一个字符
(2)erase(position);删除position处的一个字符(position是个string类型的迭代器)
(3)erase(first,last);删除从first到last之间的字符(first和last都是迭代器)
】
原文链接:https://blog.csdn.net/weixin_41969587/article/details/82587372
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
原文链接:https://blog.csdn.net/qq_40160605/article/d100ils/80150252
>>
有符号右移位,将运算数的二进制整体右移指定位数,正数高位用0补齐,负数高位用1补齐(保持负数符号不变)
正数:5>>2
1、转化为二进制:
0000 0000 0000 0000 0000 0000 0000 0101
2、整体右移指2位数
0000 0000 0000 0000 0000 0000 0000 0001
>>>
无符号右移位,不管正数还是负数,高位都用0补齐(忽略符号位)
结构体初始化
结构体可以通过多种方式进行初始化。下面是一些常见的初始化方法:
使用大括号({})进行初始化:
struct Person {
std::string name;
int age;
};
Person p = {“Tom”, 20}; // 使用大括号进行初始化
使用构造函数进行初始化:
struct Person {
std::string name;
int age;
Person(std::string n, int a) : name(n), age(a) {} // 定义构造函数
};
person(string name, int age):name(" "), age(0){}
Person p(“Tom”, 20); // 使用构造函数进行初始化
使用C++11新引入的列表初始化(也称为统一初始化):
struct Person {
std::string name;
int age;
};
Person p{“Tom”, 20}; // 使用列表初始化
map
它提供一对一,每个关键字只能在map中出现一次,
对数据自动排序的功能,所以在map内部所有的数据都是有序的,
自动建立Key - value的对应。key 和 value可以是任意你需要的类型。
根据key值快速查找记录,查找的复杂度基本是Log(N)
快速插入Key -Value 记录。
快速删除记录
根据Key 修改value记录。
遍历所有记录。
map<int, string> mapStudent;
mapStudent.insert(pair<int, string>(1, "student_one"));
map<int, string>::iterator iter;
for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
cout<<iter->first<<' '<<iter->second<<endl;
set清除用clear
set自定义排序
1.struct CustomCompare {
bool operator()(int a, int b) const {
// 自定义比较规则,例如降序排列
return a > b;
}
};
std::set<int, CustomCompare> s;
2.bool customCompare(int a, int b) {
// 自定义比较规则,例如降序排列
return a > b;
}
std::set<int, bool(*)(int, int)> s(customCompare);
快速幂 + - * 先取 %
2^34324
a[0] = 1;
a[1] = 2;
101111
int -2^31 ~ 2^31 - 1
int 类型通常用于表示 32 位整数,其范围是 -2,147,483,648 到 2,147,483,647。具体范围可能因编译器和平台而异,但至少保证在 -32,767 到 32,767 之间。 1e10
long long
long long 类型用于表示 64 位整数,其范围是 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。 1e19
最小公倍数
用 辗转相除法算最大公约数 b|a (b为a的因数, a为b的倍数)
当 b | a , gcd(a, b) = gcd(a, a - b) gcd(a, b) = gcd(b, a % b)
a, b, tmp
whie(!b)
{
tmp = a % b;
a = b;
b = tmp;
}
##nim博弈
有若干堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)。
这游戏看上去有点复杂,先从简单情况开始研究吧。
如果轮到你的时候,只剩下一堆石子,那么此时的必胜策略肯定是把这堆石子全部拿完一颗也不给对手剩,然后对手就输了。
如果剩下两堆不相等的石子,必胜策略是通过取多的一堆的石子将两堆石子变得相等,以后如果对手在某一堆里拿若干颗,你就可以在另一堆中拿同样多的颗数,直至胜利。
如果你面对的是两堆相等的石子,那么此时你是没有任何必胜策略的,反而对手可以遵循上面的策略保证必胜。
如果是三堆石子……好像已经很难分析了,看来我们必须要借助一些其它好用的(最好是程式化的)分析方法了,或者说,我们最好能够设计出一种在有必胜策略时就能找到必胜策略的算法。
异或 运算符
##nim博弈
https://zhuanlan.zhihu.com/p/616286372
当先手遇到的异或的值不为0的话,一定可以拿走一些石子使得后手面对的局面异或值为0,且后手不管怎么拿留给先手的异或值必然不为0
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=image-4.png&pos_id=img-xxPKQIxS-1698454585305
Georgia and Bob
https://blog.csdn.net/weixin_43311695/article/details/104570854
将相邻两棋子间的距离当做石子,因为每次一轮移动即可将这两棋子放在入口,则根本不用考虑他在哪个位置 只需要考虑 间距 因为不允许反超 相对位置不变且移动相当于石子减少