基本语法:#define解放双手?

本文介绍了C++中#define宏定义的三种用法:定义常量、替换语句和带参数的函数。通过示例展示了cerr、ios::sync_with_stdio等常用宏,并提醒注意不要在程序中直接使用宏定义的写法。同时,讨论了template模板、运算符重载、对拍调试码以及STL标准库的常见应用,如stack、queue、priority_queue、vector、set等容器及其功能。

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

hello大家好,今天又给大家带了c++的冷知识,本期我们要讲的是基本语法。

1 #define宏定义:解放肝帝!

有时候我们经常会重复写一些代码,尤其是类型比较长,简直痛不欲生……

这时候我们就有了一个解放双手的方式:#define宏定义。

define有三种方式:

1,定义常量

这个就是基本的,跟const是一样的效果,不一样的是它能自动识别你该用int还是用long,只是常量的值是不能变得。

2,替换语句

那些非常长的语句,比如快读,ios::sync_with_stdio……一大堆,这时候可以定义一个宏,直接替换。

3,带参数换函数

对于函数定义长的,就用这个,省时省力。也是能自动判断参数类型的,只是要注意括号。

这里给大家一些大神常用的:

1,cerr<<__LINE__<<' '<<__FUNCTION__<<endl;这个是输出他在哪一行,用来调试

2,ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) 关闭同步指令,调试用

3,rt<<1(左子节点)、rt<<1|1(右子节点) 线段树问题的常客,记住!

4,freopen(FILENAME".in","r",stdin),freopen(FILENAME".out","w",stdout) 输入输出文件,非常实用的小技巧,OI竞赛必须加他!

5,((x)&(-(x))) 这个在树状数组常用。

注意:以上写法是前面加#define 宏名 之后的语句的写法,程序中不能这么写!

这只是加快速度的小技巧,根据个人喜好用就行。

来个小题:

A+B 肝帝版

输出n组a+b的结果,n不超过5000000.

一般人估计看见数据量就蒙圈了:五百万组!!

众所周知,读取最快的就是scanf和printf,还有getchar,putchar,这题数据量这么大,显然用getchar那一组。但是!getchar只能对单个字符进行处理。

怎么办?搞个小知识:把数字看成数字的字符串,再用getchar不就快了,只需要输入后在转换就可以了。

这里注意,你不用cin和cout就别关同步!要不然就运行不了了。

还有,快读一般是用不到,而且他本质是字符串,可以被空格卡崩。所以大家在数据量没那么大时别用快读。

2 template:解放参数类型!

他也可以自动判断类型,基本写法为

template<typename T>

(函数本体)

这样用T表示类型,就不用再在类型上纠结了。

这个可以优化快读,但是卡服解决不了。

3 运算符重载:自定义加法?

这个适用于结构体,可以在结构体内外重载。

基本写法:

类型 operator运算符 (形参列表)

注意形参前面要加const 类型&。结构体外面是要加形参的那种,结构体里面可以不加。。

常用于结构体排序和STL。因为这样可以直接sort结构体。注意set<node>存结构体时必须有序且必须在参数前面加const&。

4 对拍调试码:肝帝的快乐!

对拍常用于竞赛或者自己在家写。比如你写了一个优化算法,这时候用暴力方法当做对拍代码再做一遍。因为暴力方法十有八九是对的,所以只需要答案相比较就行。

这里注意,对拍只能保证结果是对的,他不管过程,像程序员的两个噩梦TLE和MLE他就不会管,遇上橙题或红题只能一行一行看。

注意这里要有一个数据生成器来帮忙,具体模板如下:

(gen.cpp数据生成器)
int main()
{
    srand(time(0));
    int n,m,S;
    n=rand()%1000+1;
    m=rand()%2000+1;
    S=1;
    printf("%d%d%d\n",n,m,S);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        u=rand()%n+1;
        v=rand()%n+1;
        w=rand()%1000000000;
        printf("%d%d%d\n",u,v,w);
    }
}

下面是对拍程序模板:

int main()
{
    int t=10;
    while(t)
    {
        t--;
        system("gen.exe>data.in");
        system("brute.exe<data.in>brute.out");
        system("std.exe<data.in>std.out");
        if(system("fc brute.out std.out")) break;
    }
    if(t==0) cout<<"yes";
    else cout<<"error";
}

注意,这里data.in是之前gen.cpp生成的文件,按照输入格式来;这里std.cpp指的是你最初写的需要调试的代码,brute.cpp指的是用来对比的暴力代码。这里的数据生成器采用了C++随机生成,具体方法以后再讲。再解释一下,rand()函数是取随机数的,system(fc a b)是比较a和b的文本是否一样。而system(a>b)是把a给b,(b<a)是把b给a

我这里着重说下rand(),基本格式是rand()%N,代表范围0到N-1;%N加1就是1到N的范围。前面加的srand()是种子(简单理解成mc那个种子,用来生成对应区间),time(N)是时间戳。
要取范围【n,m】得数,是rand()%(m-n+1)+n。

还有一种“真随机数”——mt19937。

基本写法有点长,耐心看完:

unsigned seed=std::chrono::system_clock::now().time_since_epoch().count();
mt19937 rand_num(seed);

没有十年码龄学不来啊!

接下来给大家一个常用代码:

生成1——n随机排列

for(int i=1;i<=10;i++) a[i]=i;
unsigned seed=std::chrono::system_clock::now().time_since_epoch().count();
shuffle(a+1,a+11,default_random_engine(seed));

此时a[]数组里存了一个随机排列。shuffle函数用来打乱区间内的元素。

我们还能用并查集加rand生成随机树,进而生成随机图。

5,STL标准库:C++人性化的一面

众所周知,C++有个标准库叫STL,我之前也讲过。

复习一下,我列几个常用的STL关键字:

算法函数:<algorithm>

栈:stack

队列:queue

优先队列:priority_queue,队尾压进去的自动排序,出队可以是升序也可以是降序,升序的是priority_queue<int,vector<int>,greater<int>>q;

降序的则是priority_queue<int,vector<int>,less<int>>q;

注意优先队列可以插入运算符重载后的结构体和pair数对。

双端队列:deque 这个比较特殊,两边都可以进或者出,所以多了push_back、push_front、pop_back、pop_front四个操作,分别是在队首队尾入队和出队。单调队列用的就是他。

向量:vector 本质可以理解为大小能改的数组,啥都能放。可以直接下标访问,也可以push_back,还有insert(it,x)是迭代器指向的位置插入x,erase(it)是把迭代器指的删了。

集合:set 不重复的数据,自动去重

无序集合:unordered_set 无序去重

多重集合:multiset 有序不去重,能二分查找,查到的是迭代器的类型

映射:map 映射是一种关联方式,比如map<int,int>a是两个int型的映射,他也可以用下标访问。但是当建立映射时,map中的键是唯一的,像变量一样。另外函数clear是清空,counnt(x)是查找键为x的数的数量。map的底层是红黑树(别问我红黑树是啥,因为我也不知道!)还有,map的迭代器要用->first取第一关键字,->second取第二关键字。

无序映射:unordered_map ,它的底层实现是哈希表,不是自己有序的,增删改查操作比map快。注意insert时必须插入pair形式。

多重映射:multimap 按照插入的顺序排序,先排第一关键字再排时间,而且键值能重复用,形成一对多的数据结构。注意erase会把所有带这个键值的全删掉。

数对:pair 包含两个数据值,两个数据的类型可以不一样。三个值能嵌套pair。它能够直接排序,后面会见他的排序规则。注意尾部“ > >”两个尖括号一定要加空格,要不就跟右移混了。有一个函数是make_pair,对已有的元素构造新的数对。注意不是int会按照字典序排序。

STL库把一切都隐藏,只留了调用接口。标红的是重点,必须记住!

STL可以理解为C++创始时候人性化的一面——于是,就不用手写栈和队列了。好吧,开个玩笑而已。

上面讲了一个迭代器,后面讲。

接下来依然是重点,是algorithm库的几个常用函数:

1,unique(起始下标,结束下标) 对应区间中的重复数据挑出来放在区间末尾,注意这个区间内的数据必须是有序的。

2,lower_bound/upper_bound(起始下标,结束下标,num(要找的数据))

lower是第一个大于等于num的位置,upper则是第一个大于num的位置。注意这个区间依然是必须有序的。

3,next_permutation/prev_permutation(起始下标,结束下标)是找全排列,next是求下一个全排列,prev是求上一个全排列。

4,swap(a,b)交换a和b的值,再也不用写三行还多个变量了。

接下来讲迭代器,它可以看作是一个指针,作为容器和算法间的媒介,他可以指向一个元素。迭代器的定义方式是::iterator it;

因为它类似于指针存地址,所以赋值要加*号。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值