为什么偏爱mod 1e9+7呢?using namespace std又是什么?

本文解析了算法竞赛中为何常用Mod1e9+7进行取模运算的原因,并深入探讨了C++中使用using namespace std的意义与作用。

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

欢迎访问https://blog.csdn.net/lxt_Lucia~~
宇宙第一小仙女\(^o^)/~~萌量爆表求带飞=≡Σ((( つ^o^)つ~ dalao们点个关注呗~~

-------------------------------我只是一条可爱哒分界线---------------------------------

 

突然想到了,就查了一下~

一.mod 1e9+7

相信在算法题目中,大家遇到过很多很多次这样的情况:

由于结果可能较大,请将结果mod 1e9+7,即mod 1000000007 。

or

( a * b ) % c = [ ( a % c ) * ( b % c ) ] % c ,而这个c最常见的还是1e9+7。

每当数字结果是比较大的时候,我们总是喜欢mod 1e9+7,那么你知不知道为什么偏偏喜欢

mod 1e9+7 而不是别的数呢?

今天忽然想到了这个问题,于是查了一下资料,参考材料较多,恕不能一一列出,在此感谢度娘和各位dalao~~

 

大概≖‿≖✧是因为......

 

≖‿≖✧是因为......

 

≖‿≖✧因为......

 

1)减少冲突。

       首先,1e9+7是一个大的数,int32位的最大值为2147483647,所以对于int32位来说1000000007足够大int64位的最大值为2^63-1,对于1000000007来说它的平方不会在int64中溢出所以在大数相乘的时候,因为(a∗b)%c=((a%c)∗(b%c))%c,所以相乘时两边都对1000000007取模,再保存在int64里面不会溢出 。◕‿◕。

       其次,1e9+7是一个质数,在模素数p的情况下a*n(a非p的倍数)的循环节长为p,这是减少冲突的一个原因。另一方面模素数p的环是无零因子环,也就是说两个非p倍数的数相乘再模p不会是零(如果是0的话,在多个数连乘的情况下会大大增加冲突概率)。比如说如果所有的结果都是偶数…你模6就只可能出现0, 2, 4这三种情况…但模5还是可以出现2, 4, 1, 3这四(4=5-1)种情况的… hash表如果是用取模的方法也要模一个大质数来减少冲突,出题人也会这样来 希望减少你“蒙对“的概率。

2)模1e9+7相加不爆int,相乘不爆long long

3)允许“除法”操作(乘以乘法逆元)

        就是模素数所成的环还是个域,因而允许“除法”操作(乘以乘法逆元),模非素数就没有这个性质。
        一般来说x的选取只要10^x+7保证比初始输入数据的范围大就可以了。比如有些数据范围小的题为了避免用long long而把模数设定为10007。至于为什么要用10^x+7,大概是因为这种patten多为素数而又比较好记吧。

4)出题人的本意不在高精度

       首先有很多题目的答案是很大的,然而出题人的本意也不是让选手写高精度或者Java,所以势必要让答案落在整型的范围内。那么怎么做到这一点呢,对一个很大的质数取模即可(自行思考为什么不是小数)。那么如果您学过哈希表的设计的话,应该知道对质数取模的话,能尽可能地避免模数相同的数之间具备公因数,来达到减少冲突的目的。那么有个很大的且好记的质数1e9+7(包括它的孪生素数1e9+9)。

 

二.using namespace std

咱们在写完头文件的时候,都会写上using namespace std; 这一句,想最初学快排的时候,我们只知道用快排什么的,都要在头文件下面加上这一句,才能使用。那么可曾想过,这一行是做什么的呢?为什么要加这一行呢?

 

std 是一个命名空间 .. 

不同的命名空间可以有相同的类名被定义 ..

using namespace std;

就是指明下面的程序使用std,如果不用这句指明的话就要用std::string(string是std空间中定义的也可以在全局空间中定义,只要名字空间不一样即可....)

否则可以默认名字空间中有std,便不用std::来修饰

它是C++新标准中有的,解决多人作编大程序时名字冲突问题。比如A B两个班都有叫张三的人,你要使用A班的张三,必然要先指明是A班这个名字空间(namespace),然后你对张三的所有命令才能达到你的预想,不会叫错人。

如果你用#include<iostream.h>就不需写这句话(旧标准)。但是如果你用#include<iostream>就必须要写。

 

所谓namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。
一 :

<iostream>和<iostream.h>是不一样,前者没有后缀,实际上,在你的编译器include文件夹里面可以看到,二者是两个文件,打开文件就会发现,里面的代码是不一样的。

后缀为.h的头文件c++标准已经明确提出不支持了,早些的实现将标准库功能定义在全局空间里,声明在带.h后缀的头文件里,c++标准为了和C区别开,也为了正确使用命名空间,规定头文件不使用后缀.h。

因此,当使用<iostream.h>时,相当于在c中调用库函数,使用的是全局命名空间,也就是早期的c++实现;当使用<iostream>的时候,该头文件没有定义全局命名空间,必须使用namespacestd;这样才能正确使用cout。

二:

所谓namespace,是指标识符的各种可见范围。

C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。

由于namespace的概念,使用C++标准程序库的任何标识符时,可以有三种选择:

1、直接指定标识符。例如std::ostream而不是ostream。完整语句如下:

std::cout << std::hex<< 3.4<< std::endl;

2、使用using关键字。

using std::cout;
using std::endl;

以上程序可以写成

cout << std::hex<< 3.4<< endl;

3、最方便的就是使用using namespace std;

例如:

#include <iostream>
#include <sstream>
#include <string>
using namespace std;
这样命名空间std内定义的所有标识符都有效(曝光)。就好像它们被声明为全局变量一样。那么以上语句可以如下写:

cout << hex<< 3.4<< endl;

因为标准库非常的庞大,所程序员在选择的类的名称或函数名时就很有可能和标准库中的某个名字相同。所以为了避免这种情况所造成的名字冲突,就把标准库中的一切都被放在名字空间std中。但这又会带来了一个新问题。无数原有的C++代码都依赖于使用了多年的伪标准库中的功能,他们都是在全局空间下的。

        所以就有了<iostream.h>和<iostream>等等这样的头文件,一个是为了兼容以前的C++代码,一个是为了支持新的标准。

命名空间std封装的是标准程序库的名称,标准程序库为了和以前的头文件区别,一般不加".h"

 

using namespacestd;实际上就是告诉编译器,你类型是什么,在哪能找到。

常用的是using namespace std,就是说用C++的标准名字空间。

你也可以引用你自己的名字空间。比如说:

import "C:\\MyTest\\test.tlb"
using namespace CMyTest

就可以引用CMyTest内的各个类型名

看C++ prime
---------------------------------------------------------------

声明该文件使用C++标准库吧!
比如
#include <iostream>
using namespace std;
void main()
{
   cout<< "hello!"<< endl;
}

如果不用using namespace std;这句,那么
std::cout << "hello!"<<endl;
这是名字空间的问题!具体参看有关书籍吧,新版的C++ 书应该都有介绍的!

---------------------------------------------------------------

using 指示符!
这是个名字空间问题,是标准C++引入的新概念!
具体在《C++Primer》第8.6节有详细说明!
---------------------------------------------------------------

因为标准库非常的庞大,所程序员在选择的类的名称或函数名时就很有可能和标准库中的某个名字相同。所以为了避免这种情况所造成的名字冲突,就把标准库中的一切都被放在名字空间std中。但这又会带来了一个新问题。无数原有的C++代码都依赖于使用了多年的伪标准库中的功能,他们都是在全局空间下的。

所以就有了<iostream.h>和<iostream>等等这样的头文件,一个是为了兼容以前的C++代码,一个是为了支持新的标准。
---------------------------------------------------------------

名字空间,实质上也是为了方便程序在不同平台上正确的运行。
---------------------------------------------------------------

namespace是为了解决C++中的名字冲突而引入的。
什么是名字冲突呢?比如,在文件x.h中有个类MyClass,
在文件y.h中也有个类MyClass,而在文件z.cpp中要同时
引用x.h和y.h文件。显然,按通常的方法是行不能的,
那怎么办呢?引入namespace即可。

参考材料较多,恕不能一一列出,在此感谢~~

 

-------------------------------------我也是有底线的--------------------------------------

宇宙第一小仙女\(^o^)/~~萌量爆表求带飞=≡Σ((( つ^o^)つ~ dalao们点个关注呗~~

<think>好的,我现在需要解决用户在C++编译时遇到的宏定义问题,具体错误是`[Error] expected primary-expression before &#39;long&#39;`和`[Error] expected &#39;)&#39; before &#39;long&#39;`。用户提到他们试图用宏将`int`替换为`long long`,但出现了语法错误。我得仔细分析可能的原因,并给出正确的解决方案。 首先,用户可能是在宏定义中错误地使用了类型别名。例如,可能写了类似`#define int long long`的代码,这会导致预处理器在替换时出现问题。因为宏定义是简单的文本替换,直接将代码中的`int`替换为`long long`,但在某些上下文中,比如函数参数列表或类型声明中,这样的替换可能破坏语法结构。例如,函数声明`void foo(int x);`会被替换成`void foo(long long x);`,这本身是合法的,但如果在其他情况下,比如在表达式或语句中使用`int`作为变量名或其他用途,替换可能导致语法错误。 另一个可能性是用户在宏定义中使用了复杂的表达式,比如带有括号或运算符,导致替换后的代码结构不正确。例如,如果用户定义了一个带有参数的宏,而没有正确使用括号,可能会引发语法错误。例如,`#define MULTIPLY(a, b) a * b`在替换`MULTIPLY(3 + 4, 5)`时会变成`3 + 4 * 5`,这可能不符合预期,但这里的问题更可能出现在类型替换上。 接下来,我需要考虑正确的宏定义方法。在C++中,使用`typedef`或`using`来定义类型别名是更安全的方式,例如`typedef long long ll;`或者`using ll = long long;`。这样可以避免宏替换带来的问题,因为类型别名是在编译阶段处理的,而不是预处理阶段的文本替换。但用户可能因为某些原因必须使用宏,比如需要兼容旧代码或特定平台定义。 如果必须使用宏,正确的做法应该是定义一个新的标识符,而不是覆盖现有的关键字。例如,`#define int long long`这样的做法是不推荐的,因为它会改变所有`int`的出现,可能导致不可预见的错误,尤其是在包含系统头文件时,可能会破坏库函数的参数类型。正确的宏定义应该是给类型起一个新名字,例如`#define MY_INT long long`,然后在代码中使用`MY_INT`代替`int`。 但用户可能的问题在于,他们可能错误地编写了宏,比如在替换时遗漏了括号或其他必要符号。例如,如果宏定义为`#define int (long long)`,这样在替换时可能引入多余的括号,导致语法错误。例如,`int x;`会被替换成`(long long) x;`,这显然是不正确的声明语法。 再具体到用户的错误信息:`expected primary-expression before &#39;long&#39;`和`expected &#39;)&#39; before &#39;long&#39;`,这表明编译器在某个地方遇到了意外的`long`关键字。可能的情况是,用户在某个表达式中使用了宏替换后的`long long`,但由于替换的位置不正确,导致语法错误。例如,可能在函数调用中错误地替换了参数类型,或者在某个表达式中将变量类型替换为`long long`,但语法结构被破坏。 例如,考虑以下代码: ```cpp #define int long long int main() { int x = 0; return x; } ``` 这种情况下,替换是合法的,但如果代码中有其他结构,比如: ```cpp #define int long long void func(int x) { // ... } int main() { long x = 5; // 这里原本可能是int x =5,但被替换成long long x=5,但用户可能误用了long func(x); return 0; } ``` 这里可能不会直接导致问题,但如果用户在其他地方错误地混合了类型,比如在宏替换后导致重复的`long`关键字,例如`long long long x;`,这就会引发错误。或者,在宏替换时可能在某些表达式中导致问题,比如强制类型转换:`(int)x`会被替换成`(long long)x`,这可能没问题,但如果用户写成`int(x)`,替换后变成`long long(x)`,这在某些上下文中可能不被接受。 另一个可能性是用户在使用宏时,将`int`替换为`long long`,但在某些需要类型说明符的地方,替换后的类型被拆分成`long`和`long`,导致语法错误。例如,在某个表达式中,用户可能写了一个宏,例如: ```cpp #define TYPE int TYPE a = 10; ``` 然后改为: ```cpp #define TYPE long long TYPE a = 10; ``` 这本身没问题,但如果用户错误地在宏定义中加入其他符号,例如: ```cpp #define int long long ``` 然后在声明变量时: ```cpp unsigned int x; // 替换后变成 unsigned long long x,这是合法的 ``` 但如果用户写成: ```cpp long int x; // 替换后变成 long long long x,这会导致错误 ``` 这里,`long int`原本是合法的,但替换为`long long long`,显然语法错误,因为`long`重复了三次。 因此,根本问题可能在于用户使用`#define int long long`,这会导致所有`int`的出现被替换,包括在类型修饰符组合中的情况,从而产生不合法的类型声明,如`long long long`。正确的做法是避免覆盖现有的类型关键字,而是使用新的类型别名。 解决方案应该是建议用户使用`typedef`或`using`来定义类型别名,而不是使用宏替换`int`。例如: ```cpp typedef long long ll; // 或者 using ll = long long; ``` 然后在代码中使用`ll`作为类型。如果用户必须使用宏,应定义一个不会与现有关键字冲突的新标识符,例如: ```cpp#define MY_INT long long ``` 然后使用`MY_INT`作为类型。 总结,用户的错误源于错误地使用宏替换`int`为`long long`,导致在某些上下文中出现语法错误。正确的解决方法是改用类型别名,或者在必须使用宏时,正确命名以避免冲突和语法错误。</think>在C++中,使用宏定义将`int`替换为`long long`时,需注意宏是简单的文本替换机制。若错误使用`#define int long long`,会导致类型声明出现`long long long`等非法组合。以下是解决方案和原理分析: --- ### 错误原因分析 当使用`#define int long long`时: 1. 所有`int`关键字会被替换为`long long` 2. 若原始代码中存在`long int`类型声明,替换后会变成`long long long`(非法语法) 3. 在函数参数、模板等场景中可能破坏语法结构[^1] --- ### 正确解决方案 #### 方法1:使用类型别名(推荐) 用`typedef`或`using`定义安全类型: ```cpp typedef long long ll; // C风格 using ll = long long; // C++11风格 ``` 使用时直接声明变量: ```cpp ll num = 10000000000LL; // 合法且安全 ``` #### 方法2:限定条件的宏定义 若必须使用宏,需避免覆盖关键字: ```cpp #define MY_INT long long MY_INT num = 10000000000LL; // 正确声明 ``` #### 方法3:条件编译(跨平台场景) 通过预处理器判断平台特性: ```cpp #if defined(_WIN32) using my_int = __int64; #else using my_int = long long; #endif ``` --- ### 错误示例对比 **错误代码**: ```cpp #define int long long // 危险操作 long int x; // 替换后变成 long long long x(语法错误) ``` **修正后**: ```cpp typedef long long ll; ll x; // 合法声明 ``` --- ### 扩展建议 1. **避免宏覆盖关键字**:宏定义不要直接覆盖`int`/`float`等基础类型 2. **类型一致性**:修改类型后需同步调整格式化字符串(如`printf("%lld", num)`) 3. **编译器兼容性**:C++11起正式支持`long long`,旧版本需检查编译器支持 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值