因为影响板子空间,所以现在特将其从板子区分离出来,自成一文。

TIPS&WA

这里记录收集到的一些小技巧和注意事项(大部分和STL相关)。

[TIP] 获取一个整数二进制位数

正常做法是写循环位运算,

int cnt=0;
while(x){x>>=1;cnt++;}

但是如果直接用log2函数,写法上会更简洁更快(注意,测了一下,x小于1ll«16时,循环位移运算会快一点)

inline int len(ll x){//获取二进制位数(记得#include<cmath>)
    return x>0?log2l(x)+1:0;
}
//log这里不需要写int,因为返回值自动设为int了

如果你要卡常,试试下面的写法:
inline int len(ll x){
    if(x<=(1ll<<16)){
        int cnt=0;
        while(x){x>>=1;cnt++;}
    }
    return x>0?log2l(x)+1:0;
}

[TIP/WA] 位运算相关

  • 按位左移,正数负数最左边补符号位(除开符号位的最左边);按位右移,正数负数最右边都补0;无符号数当正数处理;

  • 含有对高达$10^{18}$的数进行位运算的题,在对表达式进行左移时记得开ll,比如

    1ll<<n
    
  • 使用~进行按位取反操作,包括符号位。

    可以用~构造INF:

    ll INF=(((ull)~0)>>1);
    
  • 使用__builtin_popcount()获取一个数二进制下1的个数

[TIP/WA] 获取带空格的整行输入

洛谷的字符串题总是喜欢带一堆连续空格,还要求你不能忽略这些空格,这时候就需要用到$$getline(cin,t)$$了。 (什么?不喜欢iostream?那去这里选一个你喜欢的款式:C/C++如何整行输入,我就不一一列举了)

[WA] sort函数与string数组

两个string之间的比较也是要花时间的,若数组大小为 $n$,string长度为 $m$,则sort(a,a+n)的时间复杂的是 $O(nmlogn)$

[TIP/WA] for(auto &i:a)问题

在for(auto &i:a)循环体中对i修改直接反映于原迭代体a,但是不能在这种循环体中往a继续添加元素,不然会报错。

初始化vector数组时可以利用这个特性减少代码量:

vector<int> a(n);
for(auto& i:a)cin>>i;

[TIP] nth_element()与sort()

二者都可以求数组中第 $k$ 大,sort得先对数据进行 $O(n\log n)$ 的排序,再a[k]调用,而nth_element()则只需花 $O(n)$ 的时间进行部分排序,即可使得数组中第 $k$ 大的数就位,且其左边的数都比它小,右边的数都比它大

[TIP] 和python类似的多行字符串输入

使用R"(多行内容)",即可完成,多行字符串的存储。和python的 ‘‘‘多行内容’‘‘差不多。

[WA] 取模问题

有时候可能会遇到一个式子内的取模符号太多,导致很容易丢失重要信息,比如该模的式子没有取模:

cout<<(((pre[n]-(ans>0)*ans)%M+(ans>0)*(ans*powp(2,k)%M)%M)%M+M)%M<<ET;
//某场div2上,有个ans忘记取模了,导致一直WA2,怎么也调不出问题,赛后补上这个%M就AC了:
cout<<(((pre[n]-(ans>0)*ans)%M+(ans>0)*((ans%M)*powp(2,k)%M)%M)%M+M)%M<<ET;

这时候可以考虑开一个函数来处理取模,化繁为简:

inline ll MO(ll x){return (x%M+M)%M;}
cout<<MO(MO(pre[n]-(ans>0)*ans)+(ans>0)*MO(MO(ans)*MO(powp(2,k))))<<ET;
//虽然看着好像差不了多少的样子,但是起码不用再受到%M+M)%M的折磨了

[WA] Python 的高精度问题

有些高精度的题,如果想用python来水的话,要特别注意除法的环节,python在除法时,会把结果转成float,而float型是不支持高精度运算的。所以这时候我们就需要整除。

[WA] map的遍历问题

用for(auto &[key,v]:mp){}即可

但是不知道为什么,上次写了个for(auto &[key1,v1]:mp)for(auto &[key2,v2]:mp)结果TLE了,还以为自己写的不是正解。

破案了,map的下标访问是 $O(logn)$ 的,所以TLE了

[WA] for(ll i=0;i<n;i++)问题

非必要别用ll写for循环,ll的运算比int运算慢了一倍,小心被卡常卡掉。

[WA] lower_bound()问题

这个不是“返回指向第一个小于v的元素的迭代器”的意思,而是“返回指向第一个大于等于v的元素的迭代器”的意思,要想返回第一个小于等于的元素,我建议自己手写一个二分吧。

[WA] 素数筛问题

p数组别开小了,素数只是少,不代表它小,你100000的数据,开2024大小的数组跑素数筛是想WA几次?(Luogu P1621 集合)

[TIP] 处理倍数的一个技巧

使用 (a+k-1)/k*k 可以得到大于等于a的第一个k的倍数。

[WA] 马拉车的R[]查回文子串

菜就多练,别背个板子,抄了还不会用。如果很不太会写怎么验字符串子串是不是回文串,那就直接封装个函数,然后就只需要调这个函数就行了。你问常数?我的建议是,如果你题都没法AC,那怕被卡常而省运行时间又有何用?

vector<int> PR;
inline bool PAL(int l,int r){
    return PR[r+l-1]-1==(r-l+1);
}

(Codeforce Global Round 25 E)

[WA] vector去重

vector去重的步骤是,先sort,再unique,最后erase,别把sort忘了

sort(a.begin(),a.end());
a.erase(unique(a.begin(),a.end()),a.end());

[TIP] 数据输入格式

cin可以读入含前导0的整数,比如说00013的数据,cin读到整数型变量里就变成了13;使用getchar()忽略单个字符

比如说要获取“03:12”的h和m,可以直接这样写:

int h,m;
cin>>h;getchar();cin>>m;

[TIP] 计算几何计算距离的快速写法

正常是这样写:

ld d=sqrtl((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));

有点麻烦,如果不在意函数的常数的话,可以使用cmath的函数求

ld d=hypot(x1-x2,y1-y2);

[TIP] 极简二分板子(FROM jiangly)

若数列已经按某规则划分成了左右两组,则该STL可快速返回其分界点(右边那组最靠左的元素)。

注意事项:若ck函数在给定范围内均成立,则返回的是r+1的值,如果有必要,这点记得特判一下。

//所需库:algorithm,ranges(C++20才有的,还是得记一下标准二分板子,这个只是偷懒用的)
//返回第一个使ck(x)为假的x
//(l,r+1)因为是左开右闭,所以右端应+1
int ans = *ranges::partition_point(ranges::iota_view(l, r+1),
    [&](int v) {
        return ck(v);
    });

//使用例:
#define PP(r,l,CK) *ranges::partition_point(ranges::iota_view(l, r+1),CK)

ll ans=PP(1,n,[&](int x){
    ...
    return 1;
    ...
    return 0;//ck函数直接写这里就行
});
//注意,如果你使用#define的调用方法,那么CK块里面不能出现逗号
//不然会报错。如果你真的需要在ck函数里写逗号,请auto ck=[&](int x){...};
//再PP(1,n,ck);

//最基础的两个线性二分STL:
//返回指向第一个大于v的元素的迭代器
auto mid=upper_bound(a.begin(),a.end(),v);
if(mid==a.end())//此时数组中不存在满足条件的数
else int ans=mid-a.begin();

//返回指向第一个大于等于v的元素的迭代器
auto mid=lower_bound(a.begin(),a.end(),v);

[WA] unordered类型容器

此类容器不能用end和begin来访问,乱写小心CE

[TIP] 降序排序容器

正常sort(a.begin(),a.end())是升序排序容器,如果想要倒序排序,可以用sort(a.rbegin(),a.rend())来排序,而无序写cmp。

[TIP] 判断vector内元素是否全相等

[From jiangly]

if(a==vector<int>(n,a[0]))

[WA] 并查集别用fa数组查根,要用find函数

[TIP] cp的sum函数

//begin(a)=>a.begin()
std::accumulate(begin(a), end(a), 0)

[TIP] STL前缀和

//begin(a)=>a.begin()
std::partial_sum(a.begin(), a.end(), b.begin());

[TIP] STL轮换

少用,O(n)的,你直接变换下标也行

//begin(a)=>a.begin()
std::rotate(a.begin(), a.end(), x);//x指第几号元素会被移位到第一个

[TIP] 快速离散化

保证数据两两不同时的快速离散化

std::vector<int> p(n);
std::iota(p.begin(), p.end(), 0);
std::sort(p.begin(), p.end(), [&](int i, int j){
	return a[i] < a[j];
});
//此时,p的各个元素值代表原数组对应元素排序后的序号(从小到大)
//1 8 5 3 2 -> 0 4 3 2 1

[WA] 莫队不要带log

$O(n\sqrt n \log n)$的复杂度在 $n = 2e5$ 时已经达到了 $1e9$ 级别了。

[TIP] 非常抽象的维护

    auto cmp = [&](const auto& a, const auto& b) {
        return std::make_pair(dis[a.first][a.second], a) < std::make_pair(dis[b.first][b.second], b);
    };
    std::set<std::pair<int, int>, decltype(cmp)> q(cmp);

可以在每次 insert 时,按照外部提供的数据结构重新排序。(应该是$O(\log n)$的)

[TIP] 使用set维护不相交区间集

通过一种特殊的重载,可以实现用 set 维护不相交区间集。而且能够用 .find({x, y}) 成员函数,返回第一个与 $[x, y]$ 相交的区间。

bool operator<(const Data &b) const {
	return r < b.l;
}
auto cmp = [&](const auto& a, const auto& b) -> bool {
    return a.second < b.first;//a.R < b.L
};

std::set<std::pair<int, int>, decltype(cmp)> s(cmp);

[WA] 网络流板子建边

jiangly那个网络流板子,建边不是双向的,网络流本身也不是无向边,多写题练练吧

[WA] 位运算符号优先级问题

&, ^, | 和 «,» 不同级,建议疯狂加括号

[WA] 更安全的取模

对于 $abs(a)$ 远大于模数的情况的取模,可以这样取:

(std::abs(a) / M * M + M + a) % M

[TIP] 手写CPP23特性 this auto

对于高版本 cpp,lambda函数递归可以这样写:

auto f = [&](this auto self, int x) -> void {
    if (x > 0) return;
    self(x - 1);
};

但是若版本没那么高,就用不了,但我们可以手动实现这种特性:

// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0200r0.html
namespace std {
    template<class Fun>
    class y_combinator_result {
        Fun fun_;
    public:
        template<class T>
        explicit y_combinator_result(T &&fun): fun_(std::forward<T>(fun)) {}

        template<class ...Args>
        decltype(auto) operator()(Args &&...args) {
            return fun_(std::ref(*this), std::forward<Args>(args)...);
        }
    };

    template<class Fun>
    decltype(auto) y_combinator(Fun &&fun) {
        return y_combinator_result<std::decay_t<Fun>>(std::forward<Fun>(fun));
    }
}

auto F = std::y_combinator([&](auto self, int x) -> void {
    if (x > 0) return;
    self(x - 1);
});

虽然但是,我觉得还不如直接self(self, x)算了,也就多几个字符