卡常小寄巧
由于主播最近做 Ynoi 做多了,卡常卡的有点魔怔,所以写一下这个。
读入输出
我常用的是 cin/cout
,不怎么用 printf/scanf
。
- 关掉同步流(常用手法)
- 多询问的时候,换行不要用
endl
,用\n
,endl
会附加清空缓存区的时间。 - 数据量大或者时限紧的时候手写快读快写。
正常快读快写
inline int read(){
int x=0,y=1;
char ch=getchar();
while (ch>'9'||ch<'0')y=(ch=='-')?-1:1,ch=getchar();
while (ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*y;
}
inline void print(int x){
if (x>9)print(x/10);
putchar(x%10+'0');
return;
}
只要不遇到恶心出题人给你每行后面加一千个空格就不会出大问题。
一般来说加了这俩就够了,但是 lxl 的题可能不太够,所以就用这个。
更 nb 的 fread
struct IO{
static const int S=1<<21;
char buf[S],*p1,*p2;int st[105],Top;
~IO(){clear();}
inline void clear(){fwrite(buf,1,Top,stdout);Top=0;}
inline void pc(const char c){Top==S&&(clear(),0);buf[Top++]=c;}
inline char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline IO&operator >> (char&x){while(x=gc(),x==' '||x=='\n'||x=='\r');return *this;}
template<typename T>inline IO&operator >> (T&x){
x=0;bool f=0;char ch=gc();
while(!isdigit(ch)){if(ch=='-') f^=1;ch=gc();}
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
f?x=-x:0;return *this;
}
inline IO&operator << (const char c){pc(c);return *this;}
template<typename T>inline IO&operator << (T x){
if(x<0) pc('-'),x=-x;
do{st[++st[0]]=x%10,x/=10;}while(x);
while(st[0]) pc('0'+st[st[0]--]);return *this;
}
}fin,fout;
从别人那贺过来的 fread
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
其实 Ynoi 写 fread 是不是已经成传统了来着。
这俩差多少我也没测过(目移)
有时候我会去题解里抄一个快读下来什么的(目移)
计算,变量,函数
long long
运算速度比int
慢一点,所以要卡的时候先把#define int long long
去掉,能避免ll
运算(特别是乘法和取模)就避免掉。- 如果数据范围允许,可以把一些
int
改成short
。 - 在一些情况下(比如求数列和)可以避免加一个取模一次,全加完再取一次模会更快。
- 取模运算很慢,所以加减法的取模不要用
(a+b)%mod
。改成加减法会更快。
取模优化
inline int add(int x,int y){
return x+y>=mod?x+y-mod:x+y;
}
inline int del(int x,int y){
return x-y<0?x-y+mod:x-y;
}
- 把
for
里的变量改成register int
。 - 在函数前面加
inline
。 - 传参数和数组的时候用
const int &
而不是int
。 - 对于没有负数的变量可以开
unsigned
。 - 赋值操作改格式,比如
int a=0;
改成int a(0);
(这一条不知道有没有效果) - 一些判断语句改用三目运算符(没测过,不知道效果)
if(?)?;
改成(?)&&(?);
。 - 循环展开。小矩乘不写循环,语句比较小的(比如
for(int i=0;i<2;i++)
)也把循环拆掉。一般来说循环展开 4 层左右就够了。 - 调整数组访问的顺序,顺序访问数组比随机访问快很多。可以调整多维数组下标的顺序,或者改变循环访问的顺序(尤其在矩乘中)
a++
改成++a
。
位运算
swap(a,b)
\(\to\)x^=y^=x^=y
-x
\(\to\)~x+1
abs(x)
\(\to\)x^(~(x>>31)+1)+(x>>31)
x%2==1
\(\to\)x&1
x%2==0
\(\to\)~x&1
- 乘除 2 的幂次用左移或右移代替。
对于 STL 库
push_back
改成emplace_back
(不清楚效果)lower_bound/upper_bound
改用手写二分。set/map
常数比较大,遇到卡这俩的题换个做法或者手写吧。sort
对排的比较好的数组常数大,可以先打乱再排(不清楚效果)- 如果还不行可以对值域小的数组用基数排序。
memcpy
比循环复制快。memset
把sizeof(a)
改成(n+5)*sizeof(int)
(对于需要清空的比较少的时候)__builtin
很好用,本质其实是位运算,很快。
__builtin 一家
__builtin_ctz(unsigned int x);//求 x 二进制末尾 0 的数量
__builtin_clz(unsigned int x);//求 x 二进制前导 0 的数量
__builtin_ffs(unsigned int x);//求 x 二进制末位 1 的位置
__builtin_popcount(unsigned int x);//求 x 二进制 1 的个数
__builtin_parity(unsigned int x);//求 x 二进制 1 的个数的奇偶性
其他
- 线段树启用底层分块和底层循环,会快一些。
- 提前线性求逆元。
- NTT 预先处理原根的幂。
- 分块可以调一调快长,一般有用。
- 搜索的时候
vis
不要次次清空,可以用不同的值重复覆盖标记减少清空次数。 - 建图用链式前向星,少用
vector
。
最后祝广大 OIer 早日脱离卡常苦海。