杂项

· · 个人记录

自动判断输入结束

按位取反,简单地说就是二进制1变0,0变1

由于scanf是有返回值的,且返回值为int型

特别的此处用法导致只有scanf返回-1,循环才会结束,也就是要返回EOF

while (~scanf("%d%d",&n,&m))

等效于

while (scanf("%d%d",&n,&m)!=EOF)

其实这样也行

while (cin >> x)

只有-1 补码的取反后全为0,终止循环。

未定义的行为

Undefined Behavior,简称UB

经常是在同一语句中对某个变量进行多个操作然后产生了歧义,此时不同编译器可以自己选择解释方式

如 P3141 [USACO16FEB]Fenced In P 中对数据的处理就容易出现歧义

#include <bits/stdc++.h>

using namespace std;

const int N = 3e5 + 10;
int s[N], t[N];
int n, m, a[N], b[N], r, c;
long long ans;

int main() {
    scanf("%d%d%d%d", &r, &c, &n, &m);
    for(int i = 1; i <= n; ++i) scanf("%d", &s[i]);
    for(int j = 1; j <= m; ++j) scanf("%d", &t[j]);
    sort(s + 1, s + n + 1);
    sort(t + 1, t + m + 1);

    for(int i = 1; i <= n; ++i) b[i] = s[i] - s[i - 1];
    b[++n] = r - s[n - 1];
    for(int i = 1; i <= m; ++i) a[i] = t[i] - t[i - 1];
    a[++m] = c - t[m - 1];

    swap(n, m);
    sort(a + 1, a + n + 1); 
    sort(b + 1, b + m + 1); 

    int it1 = 2, it2 = 2, h1 = 1, h2 = 1;
    ans += (long long)a[1] * (m - 1);
    ans += (long long)b[1] * (n - 1);
    while(it1 <= n && it2 <= m) {
        if(a[it1] > b[it2]) {
            ans += (long long)b[it2] * (n - h1);
            ++it2;
            ++h2;
        }
        else {
            ans += (long long)a[it1] * (m - h2);
            ++it1;
            ++h1;
        }
    }
    printf("%lld", ans);
    return 0;
}  

/*
输入:
15 15 5 2
2
5
10
6
4
11
3

本地输出:
44

洛谷IDE:
52

那究竟是什么导致的呢?

其实问题出在这里:

    for(int i = 1; i <= n; ++i) b[i] = s[i] - s[i - 1];
    b[++n] = r - s[n - 1];
    for(int i = 1; i <= m; ++i) a[i] = t[i] - t[i - 1];
    a[++m] = c - t[m - 1];

第二行和第四行的++m和m-1纠缠在了一起,本地的gcc编译器和洛谷ide的clang各执一词,才发生了交了十多次都是全wa的悲剧。

而解决的方法也很简单,只要像下面这样拆开来就好了

    for(int i = 1; i <= n; ++i) b[i] = s[i] - s[i - 1];
    b[n + 1] = r - s[n];
    ++n;
    for(int i = 1; i <= m; ++i) a[i] = t[i] - t[i - 1];
    a[m + 1] = c - t[m];
    ++m;

所以之后遇到++某个变量这种操作,最好进行一下隔离,特别是不要和其它对这个变量的操作放在同一条语句中……

Linux调试台小芝士

最常见的c++编译语句:

g++ test.cpp -o test
//编译器名称 源文件名称 编译指令 可执行文件名
//可执行文件名要放在编译指令的后面

分段编译

一个C/C++源代码要变成一个可执行文件,需要经过预处理(Pre-processing)-编译(Compiling)-汇编(Assembling)-链接(Link)

基本流程为:

test.cpp --预处理--test.i --编译-- test.s --汇编-- test.o --链接-- test.exe

-E 选项使用g++/gcc将源代码预处理后不执行其他动作。

下面的命令将test.cpp预处理,并在标准输出中显示

g++ -E test.cpp 

后面加上 -o 选项表示将源代码预处理后输出在指定文件中,比如test.i:

g++ -E test.cpp -o test.i

-S 选项使用g++/gcc将预处理后的文件编译,翻译成汇编代码。只编译不汇编

下面的命令将会编译test.i文件,并自动在当前文件夹生成test.s文件

g++ -S test.i

若要指定其他输出名,则需 -o 指定,比如生成名为xxx.s的汇编代码文件

g++ -S test.i -o xxx.s

-c 选项将编译生成的test.s文件生成二进制目标代码

下面的命令将在当前文件夹自动生成test.o的二进制目标代码文件

g++ -c test.s

如果要指定输出文件名,则需 -o 指定,比如生成xxx.o的二进制目标代码文件

g++ -c test.s -o xxx.o

链接阶段是将相关的目标文件链接起来,形成一个整体,生成可执行文件

无选项链接

下面的命令会把二进制目标文件test.o所需的相关文件链接成一个整体,并在当前文件夹自动生成一个名为a.out的可执行文件

g++ test.o

如果要执行这个可执行文件,需要输入命令

./a.out

当然也可以指定生成的可执行文件的文件名

g++ test.o -o test.exe

很明显,这当然可以,毕竟前面写过了

非指定文件名(生成a.out):

g++ test.cpp

指定文件名(要加-o选项):

g++ test.cpp -o test

也可以将多个源代码编译链接成一个可执行文件

下面的命令将test.cpp直接在当前文件夹生成a.out可执行文件,若要指定文件名,可使用 -o 选项

//应该是给一些项目用的,反正咱应该暂时用不到

//直接生成
g++ test1.cpp test2.cpp 

//规定文件名
g++ test1.cpp test2.cpp -o test.exe

编译选项

一般有如下几个(放的位置自己琢磨):

-o
//指定可执行文件名,可执行文件名要接在后面

-std=c++14
//以c++14的标准编译,拥有c++14的特性

-O2
//O2优化

-lm
//某数学库,似乎没啥用

-Wall
//显示所有警告

-W
//不显示警告

中断运行时的程序

当遇到死循环或者要终端的时候

用ctrl + z 或者是 ctrl + c 中断运行

__int128

int128除了比较长之外没啥用处……

而且仅有四则运算功能……

而且还要手写输入输出……

输入输出板子:

#define int __int128
inline void read(int &n){
    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();
    }
    n=x*f;
}
inline void print(int n){
    if(n<0){
        putchar('-');
        n*=-1;
    }
    if(n>9) print(n/10);
    putchar(n % 10 + '0');
}
#undef int

例题(见第二题)

gdb调试器