卡常小寄巧
由于主播最近做 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+1abs(x)\(\to\)x^(~(x>>31)+1)+(x>>31)x%2==1\(\to\)x&1x%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 早日脱离卡常苦海。

浙公网安备 33010602011771号