常数优化技巧
UnionRE
·
·
算法·理论
卡常
这个其实很多人写过了,汇总一下。
STL
STL大部分比较慢。所以我们可以优化。
map
因此可以优化。
常见的就是`unordered_map`优化。这是一种哈希,所以可能被卡。
当然还有`pb_ds` ($\text{Policy-Based Data Structures}$) 优化。
```cpp
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace __gun_pbds;
```
`pb_ds`提供两种哈希,`cc_hash_table`和`gp_hash_table`。
> 其中cc开头为拉链法,gp开头为探测法,个人实测探测法稍微快一些。啥?操作?其实就和map差不多,支持[ ]和find。
>
> map的总时间复杂度是$O(n\log n)$的,而hash_table的总时间复杂度仅为$O(n)$!
>
> $\qquad\qquad\qquad\qquad\qquad\qquad$ ——[《比STL还STL?——平板电视》]([比STL还STL?——平板电视 - 洛谷专栏](https://www.luogu.com.cn/article/zi85ltjk))
`cc_hash_table`在随机数据下的表现较差,时间约为3倍`gp_hash_table`,空间约为8倍。
这两种哈希直接替换`map`即可,支持`[]`和`find()`,不支持`count()`。
定义方法同`map`。
顺便一说,[《关于NOI系列活动中编程语言使用限制的补充说明》](https://www.noi.cn/xw/2021-09-01/735729.shtml),使得使用pb_ds有法可依。体现了规则不是一成不变的,良法需要体现社会发展规律……
### set / multiset
已经很好了。
### priority_queue
还是`pb_ds`。 #include<ext/pb_ds/assoc_container.hpp> #include <ext/pb_ds/priority_queue.hpp> __gnu_pbds::priority_queue<Type, Compare, Tag, Allocator>//不要using namespace,不然会歧义(std::priority_queue)
* `Compare`:大根堆`less<Type>`,小根堆`greater<Type>`。
* `Tag`: pairing_heap_tag thin_heap_tag binomial_heap_tag rc_binomial_heap_tag binary_heap_tag
`paring_heap_tag`最快。
| | push | pop | modify | erase | Join |
| --- | --- | --- | --- | --- | --- |
| `pairing_heap_tag` | $O(1)$ | 最坏 $\Theta(n)\small ^1$ 均摊 $\Theta(\log(n))$ | 最坏 $\Theta(n)$ 均摊 $\Theta(\log(n))$ | 最坏 $\Theta(n)$ 均摊 $\Theta(\log(n))$ | $O(1)$ |
| `binary_heap_tag` | 最坏 $\Theta(n)$ 均摊 $\Theta(\log(n))$ | 最坏 $\Theta(n)$ 均摊 $\Theta(\log(n))$ | $\Theta(n)$ | $\Theta(n)$ | $\Theta(n)$ |
| `binomial_heap_tag` | 最坏 $\Theta(\log(n))$ 均摊 $O(1)$ | $\Theta(\log(n))$ | $\Theta(\log(n))$ | $\Theta(\log(n))$ | $\Theta(\log(n))$ |
| `rc_binomial_heap_tag` | $O(1)$ | $\Theta(\log(n))$ | $\Theta(\log(n))$ | $\Theta(\log(n))$ | $\Theta(\log(n))$ |
| `thin_heap_tag` | $O(1)$ | 最坏 $\Theta(n)$ 均摊 $\Theta(\log(n))$ | 最坏 $\Theta(\log(n))$ 均摊 $O(1)$ | 最坏 $\Theta(n)$ 均摊 $\Theta(\log(n))$ | $\Theta(n)$ |
**【注】:**
$1:$ $\Theta(g(x))$ 时间复杂度表示方法,表示确界。
* `Allocator` 空间配置器,一般不填。
## 常规操作
以下内容中,$A$>$B$ 表示$A$比$B$快。(除`>`和$>$外)
1. `i++`$\to$`++i`其实不必要。
2. 矩阵乘法,`for ikj`比`for ijk`快得多。
3. `#define`>`constexpr`>`const`(表示常量时)。
4. 快读快输。
``` cpp
#ifdef __linux__
#define gc getchar_unlocked
#define pc putchar_unlocked
#else
#define gc _getchar_nolock
#define pc _putchar_nolock
#endif
inline int read(){
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=(x << 1) + (x << 3)+ch-48;ch=getchar();}
return x*f;
}//快读
inline void write(int x){
if(x<0){putchar('-');x=-x;}
if(x>9)write(x/10);
putchar(x%10+'0');
}//快输
```
OI wiki
```cpp
void write(int x) {
static int sta[35];
int top = 0;
do {
sta[top++] = x % 10, x /= 10;
} while (x);
while (top) putchar(sta[--top] + 48); // 48 是 '0'
}
```
不同type输出时,用这个(先不要`#define int long long`)
namespace fast_io{
const int fread_cnt=(1<<20);
const int fwrite_cnt=(1<<20);
class fast_read{
private:
char buf_in[fread_cnt],*p1,*p2;
inline char gc(){return (p1==p2&&(p2=(p1=buf_in)+fread(buf_in,1,fread_cnt,stdin),p1==p2)?EOF:*p1++);}
inline bool read_bool(){char ch=gc();while(ch<'0'||ch>'1'){ch=gc();}return (ch=='1');}
inline char read_ch(){char ch=gc();while(ch==' '||ch=='\r'||ch=='\n'||ch=='\t'){ch=gc();}return ch;}
inline string read_string(){
string s;char ch=gc();while(ch==' '||ch=='\r'||ch=='\n'||ch=='\t'){ch=gc();}
while(ch!=' '&&ch!='\r'&&ch!='\n'&&ch!='\t'){s+=ch;ch=gc();}return s;
}
template <typename T>
inline T read_int(){
T x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9'){f=(ch=='-')?-f:f;ch=gc();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
return x*f;
}
template <typename T>
inline T read_unsigned(){
T x=0;char ch=gc();while(ch<'0'||ch>'9'){ch=gc();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
return x;
}
template <typename T>
inline T read_float(){
T x=0,f=1,c=1;char ch=gc();
while(ch<'0'||ch>'9'){f=(ch=='-')?-f:f;ch=gc();}
while(ch>='0'&&ch<='9'){x=x*10+(ch^48);ch=gc();}
if(ch=='.'){ch=gc();while(ch>='0'&&ch<='9'){c/=10;x+=c*(ch^48);ch=gc();}}
return x*f;
}
public:
fast_read& operator >> (bool &valx){valx=read_bool();return *this;}
fast_read& operator >> (char &valx){valx=read_ch();return *this;}
fast_read& operator >> (string &valx){valx=read_string();return *this;}
fast_read& operator >> (float &valx){valx=read_float<float>();return *this;}
fast_read& operator >> (double &valx){valx=read_float<double>();return *this;}
fast_read& operator >> (long double &valx){valx=read_float<long double>();return *this;}
fast_read& operator >> (short &valx){valx=read_int<short>();return *this;}
fast_read& operator >> (int &valx){valx=read_int<int>();return *this;}
fast_read& operator >> (long &valx){valx=read_int<long>();return *this;}
fast_read& operator >> (long long &valx){valx=read_int<long long>();return *this;}
fast_read& operator >> (unsigned short &valx){valx=read_unsigned<unsigned short>();return *this;}
fast_read& operator >> (unsigned int &valx){valx=read_unsigned<unsigned int>();return *this;}
fast_read& operator >> (unsigned long &valx){valx=read_unsigned<unsigned long>();return *this;}
fast_read& operator >> (unsigned long long &valx){valx=read_unsigned<unsigned long long>();return *this;}
}fin;
class fast_write{
private:
char buf_out[fwrite_cnt],*p=buf_out;
inline void write_bool(bool x){char ch=(x==1)?'1':'0';pc(ch);}
inline void write_ch(char x){char ch=x;pc(ch);}
inline void write_string(string s){for(string::iterator it=s.begin();it!=s.end();it=next(it)){pc(*it);}}
template <typename T>
inline void write_int(T x){
if(x<(T)0){pc('-');x=-x;}
if(x>=10){write_int(x/10);}pc((x%10)^48);
}
template <typename T>
inline void write_unsigned(T x){
if(x>=10){write_unsigned(x/10);}pc((x%10)^48);
}
template <typename T>
inline void write_float(T x){
if(x<(T)0)pc('-'),x=-x;
write_int((int)x);pc('.');x-=(int)x;
while(x!=0){x*=10;pc((int)x^48);x-=(int)x;}
}
public:
inline void pc(char ch){if(p-buf_out==fwrite_cnt){fwrite(buf_out,1,fwrite_cnt,stdout),p=buf_out;}*p=ch;++p;}
inline void flush(){fwrite(buf_out,1,p-buf_out,stdout);p=buf_out;}
inline fast_write& operator << (bool valx){write_bool(valx);return *this;}
inline fast_write& operator << (char valx){write_ch(valx);return *this;}
inline fast_write& operator << (string valx){write_string(valx);return *this;}
inline fast_write& operator << (float valx){write_float<float>(valx);return *this;}
inline fast_write& operator << (double valx){write_float<double>(valx);return *this;}
inline fast_write& operator << (long double valx){write_float<long double>(valx);return *this;}
inline fast_write& operator << (short valx){write_int<short>(valx);return *this;}
inline fast_write& operator << (int valx){write_int<int>(valx);return *this;}
inline fast_write& operator << (long valx){write_int<long>(valx);return *this;}
inline fast_write& operator << (long long valx){write_int<long long>(valx);return *this;}
inline fast_write& operator << (unsigned short valx){write_unsigned<unsigned short>(valx);return *this;}
inline fast_write& operator << (unsigned int valx){write_unsigned<unsigned int>(valx);return *this;}
inline fast_write& operator << (unsigned long valx){write_unsigned<unsigned long>(valx);return *this;}
inline fast_write& operator << (unsigned long long valx){write_unsigned<unsigned long long>(valx);return *this;}
inline fast_write& operator << (fast_write& (*func)(fast_write&)){return func(*this);}
}fout;inline fast_write& endl(fast_write& fastout){fastout.pc('\n');fastout.flush();return fastout;}
}using namespace fast_io;
5. `bitset`
逆天时间复杂度,洛谷测评机 $O(\frac{n}{32})$ 。STL神器!
6. 位运算
`(l+r)>>1` > `(l+r)/2`。
7. 手写内置函数
```cpp
int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
```
8.