细节决定成败(竞赛错题经验总结)

整理的算法模板合集: ACM模板


没有灵异事件,只有我是傻逼

图论的数组,涉及到点的开N,涉及到边的开M,开多了会TLE!根据题目要求设定,

凡是涉及到位运算的我都要累括号!!!位运算的优先级太糟糕了

数据过大的时候(例如 2000001)如果没有返回值的函数写成int就会RE ! !

多组输入别忘了初始化!!!
二分debug大法好

果然是要多注意for循环,边界呀什么的,凡是涉及到多种数据类型的一定检查好数据是否有误
能用memset就用memset,一般不循环赋初值。memset是 O ( n ) O(n) O(n)但是常数比较小

写了一堆函数完全正确,不调用,一直WA还找不出bug那不是傻子嘛
如果数据比较大(5e5),wa了,很有可能是因为没开ll!

网络流一旦wa了很可能是因为tot没有置为1,所以为了解决这个问题,我决定以后还是学习大佬们的习惯,每次把head数组置为-1吧,tot最后再++,这样tot就不用置为1了 !所以现在要养成习惯每次写链式前向星的时候都要初始化!!!

手写队列有两套,一定要写标准,不然会WA的很难看,以后就写tt = -1

如果有思路完全正确但是还是一直WA的话,先思考一下几个方面

  1. 仔仔细细地把题再看两遍!!!!!
  2. for循环是否有问题(有可能是不该加++i,有时候应该循环m而不是n)
  3. 数据类型是否有误(要开long long嘛,涉及到的是否都写成double了嘛,double是不是"%lf"输入"%f"输出呢)
  4. 多组数据初始化了嘛,初始化全了嘛(head别忘了tot
  5. 输入确定好了嘛,输入的是n和m还是m和n呢
  6. 变量是不是有写错的?
  7. Q w Q QwQ QwQ

我的妈 ( ↓ ) 呀,我每次WA的都好奇怪呀。

  1. 注意数据范围 ,若数组初始化为 I N F = 0 x 3 f 3 f 3 f 3 f INF = 0x3f3f3f3f INF=0x3f3f3f3f,那么要是中间有数组中数据的 + + + ∗ * 什么的必须强转成 l o n g   l o n g long\ long long long,(因为会爆 i n t int int,然后就会WA)
  2. 数组定义小了,会RE ! 每做一道题一定要先看数据范围,根据数据范围来思考应该采取的 O ( n ) / O ( l o g n ) / O ( n 2 ) O(n)/O(logn)/O(n^2) O(n)/O(logn)/O(n2)算法。有的题目(比如迷宫,线段树(x2),链接字符串,数组两边循环,主席树(x 2))给的n,m并不是实际真正储存的大小。计算的时候一定小心。
  3. 判断二进制数x的第y位是否为一, 用 x > > y & 1 x>>y\&1 x>>y&1,而不要用x&1<<y,因为后者的结果可能是2的几次幂,在if判断==1的时候很容易错
  4. memset一定要注意要赋值的是数组还是指针指向的数组,然后再写sizeof
  5. 我吐了,我定义了一个全局变量t,但是我写错了!在主函数里又定义了一遍,然后我想用的全局变量就变成局部变量了!codeblocks是不会报错的!然后我就WA了,一百多行的代码找了好久才找到这个bug — 同时定义的话局部变量会覆盖全局变量,并不是编译错误
  6. 注意一点,图论的TLE不一定就是因为死循环,很可能是因为链式前向星的数组没有开二倍,或者线段树没有开四倍,或者线段树合并的时候没有开50倍
  7. 在使用成对变换来找到起点的终点和终点的起点的时候( i i i 和 i ^ 1 )(遍历一条路径的时候),要从2开始(也就是初始化 t o t = 1 tot = 1 tot=1),2于3,4于5,不知道为啥从0开始会WA。
  8. 规定好,手写栈的时候第一个元素是stk[1],也就是初始化top = 0 ,stk[++top] = x那么栈顶元素就是y = stk[top--]不能乱套,不然会WA的很难看还很难找到 b u g bug bug 这一类的问题很多,所以写的时候要规范化代码
  9. 今天写循环的时候碰到一个问题,发现:string.length()返回的类型是size_type。它是unsigned 类型。string::size_type它在不同的机器上,长度是可以不同的,并非固定的长度。但只要你使用了这个类型,就使得你的程序适合这个机器。与实际机器匹配。
      如果你的机器是32位的,那么 int len = str.length(),就侥幸对了。
      如果你的机器是64位的,那么你就 被fuck 了。
      unsigned 类型两个数相减,如果是结果负数,就fuck了!显然不会得到想要的结果。
  10. 在使用vector的时候,只要是输入的vector是空的,程序就会崩溃。调试了一下,发现此时 0 < vector.size()-1 这个值为true。这个时候才反应过来,stl中的.size()函数返回的是unsigned int,这个类型下进行0-1会发生越界,所以变成了一个极大值。所以以后一定要注意不要使用stl中的.size()做减法这样的操作,如果使用的话先用int来强转一下类型来防止越界。
    所以以后遇见要使用stl的返回值的时候尽量先把它摘出来
int len = G[u].size();
  1. 我决定以后头文件里再也不用define一些奇奇怪怪的东西只是为了省事了,这种不能掌握到自己手中的bug是真的难受。我又是因为全局变量重定义导致了WA…(因为我自己定义的over函数里面是int i)
  2. 要注意数据的类型,有一个要开double那么涉及到的所有的数组变量都要开double!
  3. 使用%lf读取double型的值,用%f进行显示!!
  4. 优先队列如果想变成小根堆(队头为最小值)应该这么写:
struct node{
    int id,type,dis;
    bool operator< (const node &t)const{
        return dis > t.dis;
    }
};

几天没写给忘了,de了半天的bug…

  1. 以后有什么要变换的值,比如这个并查集
	int x = e[i].u,y = e[i].v;
	int fx = Find(x),fy = Find(y); 
	if(fx == fy)continue;
	add(x,y,z),add(y,x,z);//建成一颗树
    fa[fx] = fy;

我们在这里即需要原来的点x和y,又需要Find函数找到的fx和fy,两者都在下面有用处,我们就多开一个变量fx和fy,别顺手就写成:int x = Find(e[i].u),y = Find(e[i].v);不然我就会把它们搞混了,然后就WA啦,以后遇见这种的会更新原值的都新开一个变量存,不要用老的,不然容易搞混!!!

  1. 我本来就是因为每次写网络流tot忘了初始化为1结果决定以后每次写链式前向星的时候tot都置为1,然后我这次写网络流的时候又忘了tot置1了…我决定以后还是学习大佬们的习惯,每次把head数组置为-1吧,tot最后再++,这样tot就不用置为1了,0就可以。
  2. 手写队列
int hh = 0,tt = -1;
    q[ ++ tt] = {sx,sy};
    vis[sx][sy] = true;

    while(hh <= tt){
        PII t = q[hh ++ ];
        ......
        
        q[ ++ tt] = {a, b};
              
    }
int hh = 0,tt = 0;
    q[tt ++ ] = {sx,sy};
    vis[sx][sy] = true;
    while(hh != tt){
        PII t = q[hh ++ ];
        ......

        q[tt ++ ] = {a, b};
        vis[a][b] = true;
    }
  1. memset函数如果是为long long类型的数组赋值,那么与int赋值得到的数值是不相同的。例如给int类型memset 0x3f,得到的是0x3f3f3f3f43f。而给long long 赋值得到的是0x3f3f3f3f3f3f3f3f,就是8个3f,因为long long更长,正好是int的二倍,而memset是将每一位都赋值为0x3f如果不注意就会wa。比如用memset赋值以后通过判断值是否为0x3f3f3f3f来判断是否已经被更新过,就会出现问题。因此有一种为了避免这种情况(还有精度误差),我们一般比较就用>=INF的方式来判断。
  2. 原来meset可以初始化结构体呀 >_<
  3. 如果用namespace的话,要注意在namespace0里原程序的全局变量就用不了了,如果代码有问题(变量用错了)就可能因此导致WA。
    有趣的是namespace里的全局变量也会自动初始化为0。
  4. 线段树翻转问题,因为解决办法是在翻转半区间打上标号,有标记的节点左右子树互换(实际体现就是查找的时候交换即可,整棵树实际上是不用动的),因此所有操作在寻找预定区间的时候就不能在根据当前根节点寻找预定区间,因为这里的p已经可能变了,所以我们应该再传入参数l和r来辅助寻找预定区间
  5. 注意多组数据并查集中的fa数组也要清空!
  6. 1!!!
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>

using namespace std;
const int N = 1e7 + 7;
bool a[N];
char s[N];
int stk[N], top;
int ans, num, now;
int main()
{
    scanf("%s", s);
    int len = strlen(s);
    for(int i = 0; i < len; ++ i){
        if(s[i] == '(')stk[ ++ top] = i;
        else if(top)a[stk[top]] = true, a[i] = true, top -- ;
    }
    for(int i = 0; i <= len; ++ i){
        if(a[i])now ++ ;
        else ans = max(ans, now), now = 0;
    }
    for(int i = 0; i <= len; ++ i){
        if(a[i])now ++ ;
        else {
            if(now == ans)
                num ++ ; 
            now = 0;
        }
    }
    if(ans)
        printf("%d %d\n", ans, num);
    
    else puts("0 1");
    return 0;
}

  1. 贪心
    若对于权值影响是两个维度的两个变量a和b,那么我们一般按照a*b排序的顺序贪心。
  2. 没有灵异事件,只有我是傻逼
void add(int x, int y, int z, double c, bool o = 1)
{
    ver[tot] = y;
    edge[tot] = z;
    cost[tot] = c;
    nex[tot] = head[x];
    head[x] = tot ++ ;
    add(y, x, 0, -c, 0);//这里要写0!!!不然默认1
}

一直死循环,找了两天才看出来。。。

  1. 关闭cin的ios流之后如果使用scanf就会TLE
  2. 注意有时要输入输出的都是整数,我一般的计算几何模板都是double,用%d输入到double里得到的都是0!
  3. 数组开大了也不好,会MLE…
    持续更新中…
  4. O ( 1 ) O(1) O(1)得到一个数的位数:
int main(){
    scanf("%d", &x);
    //求x的位数
    printf("%d\n", (int)log10(x) + 1);
    //求x^x的位数(x^x <= 1e100)无法计算所以利用换底公式
    printf("%d\n", x * (int)log10(x) + 1);
    return 0;//例:x = 10 -> 2(10) 11(10^10)
}

  1. 小心数据范围,有时候初始化为0x3f是不够的!需要2147483647
  2. 如果for循环中j写成i之后可能会RE数组越界,因为for完i以后forj的时候i一直++导致数组越界!!!
  3. long long的数进入变量定为int的函数会变成0!!!所以尽量所有的变量都统一
ll C(int a, int b)
{
    if(a < b) return 0;
    return fact[a] * infact[a - b] % mod * infact[b] % mod;
}

ll a, b;
cout << C(a, b) << endl;//0
  1. 多项式里基本上所有的循环都应该是limit也就是那个被补全的2的整数幂!(找了半个小时bug
  2. 注意输入k的时候要开long long,因为998244353是9e8,输入的时候只要再多一位,就是9e9,就会直接爆int,哪怕后面%998244353也会在%之前就爆了,所以要强转成ll(数学题开long long 是好习惯,但是多项式问题里会跑的慢一些)

放两位大佬的错题本,学到了很多

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:设计师小姐姐 返回首页