【Tricks】对拍效率相关

问题

在对拍的时候我们都会指定随机种子
一般大家都会使用:

srand(time(0))

然而这样会造成严重的效率问题
因为time(0)只能精确到秒级别
换一句话来说,每一秒只能进行一次有效的对拍

解决方案

使用函数:

#include<windows.h>
GetTickCount()

这是精确到ms级别的,也就是说,1s内至多可以进行1k次有效对拍了
详见:https://msdn.microsoft.com/en-us/library/windows/desktop/ms724408(v=vs.85).aspx

【Tricks】fopen 与 fscanf / fprintf

背景

今天写了一道POI的题目,血wa
要到了数据以后发现,这货需要SPJ才能测
于是研习了一下fopen、fscanf和fprintf的用法

用法

先定义一个文件指针(我不知道这货叫什么 QwQ)

FILE *data;

之后把这个文件指针指向我们需要的文件:

data = fopen("input.in","r");

然后I/O操作的时候指定一下用哪个文件指针即可:

fscanf(data,"%d",&n);
fprintf(data,"%d",n);

优越性

freopen是重定向全局的输入输出
但因为有些时候我们需要同时对多个文件进行I/O操作
所以就需要用fopen + fscanf + fprintf辣!

【算法笔记】O(1)快速乘

对于大整数乘法来讲,一般使用快速乘
但不论是按10进制拆分或是二进制拆分,其复杂度均有log
今天鏼爷提供了一种O(1)的方法:
设所求为:a*b (%c)

1) a%c, b%c <= 1e9
这样的话,O(1)直接算就好

2) 其他情况
用long double计算\(k = \left\lfloor {\frac{{ab}}{c}} \right\rfloor\)
之后输出a*b-c*k即可
因为这样的话,不会出现一个分母特别大,分子特别小的情况,所以long double的精度能够满足要求了

【Tricks】isdigit

判断一个字符c是否为阿拉伯数字,常用的写法有两种:
1)’0′ <= c && c <= ‘9’
2) isdigit(c)

编程复杂度差不多,让我们来从效率上对比一下:
1)随机的数字串(字符串中全是数字)
789456453415
2)随机的字符串(字符串的每一个字符的ascal码从1—127随机)
78945454478
3)作为流读入的判断语句
945648
ps:上述结果在不同环境下可能会有不同结果,故上述结果仅供参考

结论:
这两种写法,对于常数的影响不大,可以随便用

【Tricks】萌萌哒表情包

大家都知道,水UOj水多了以后,就会有用不完的表情包
然而现在这个版本的QQ貌似专门把表情包藏了起来,只能导出eif格式的文件
这货还不能直接解压,这让我很不爽H[O(5H1_XNFSSZ~@I6[~$VL

于是就上网搜,找到了这个东西:eif_open.zip
然后就可以导出成图片格式啦!
举个栗子 ↓
DLT9]~LZ%M9T8[_UP2T8~0M
瞬间感觉生命由黑边变成了彩色有木有? = ̄ω ̄=

【Tricks】各种类型的范围

今天看一份代码的时候,看到一个东西:

list[i].prev1 = list[i].prev2 = list[i].next1 = list[i].next2 = std::numeric_limits<size_t>::max();

然后发现这个东西,可以搞出所有基本类型的数据范围:

numeric_limits<size_t>::max();

需要

#include<limits>

详细情况见这里:http://www.cplusplus.com/reference/limits/numeric_limits/
当然,不仅可以搞出数据范围,还可以搞一点其他的东西,比如
12345453477854245874
感觉好强啊!居然有这么多彩蛋! *★,°*:.☆( ̄▽ ̄)/$:*.°★* 。

最后吐槽一下那份代码:http://paste.ubuntu.com/23101572/
感觉完全不是std,而是《浅谈c++基本语法》或者《使用基本语法教你做人》(╯‵□′)╯︵┻━┻

【Tricks】Graph

前日,在翻blog的时候,翻到一个好东西
是用来调试图论相关题目的
真的是超级好用!

原帖地址:http://blog.csdn.net/iamzky/article/details/38844917
本地备份:Graph–by zky

—————————— UPD 2016.12.13 ——————————
来更新两个找到的关于graphviz的资料:
1)http://blog.csdn.net/stormdpzh/article/details/14648827
2)https://program-think.blogspot.com/2016/02/opensource-review-graphviz.html
感觉以后写论文用这货一定逼格爆表!

—————————— UPD 2017.6.20 ——————————
我校高一神犇xehoth用java也实现了一个可视化版本
亲测比zky的好用
大家戳这里:https://github.com/xehoth/VisualGraphviz

【模板库】对拍

我之前一直是用.bat文件进行对拍,代码如下:

@echo off
:rep
echo running SJ.exe
SJ.exe > 11input.in
echo running bc.exe
bc.exe < 11input.in > 12bc.out
echo running me.exe
me.exe < 11input.in > 12me.out
fc 12me.out 12bc.out > nul
if not errorlevel 1 goto rep

但有一个我很想实现的功能:对拍时显示运行时间
于是今天考试的时候折腾了一下,搞出了下面这个东西:

#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<windows.h>
using namespace std;

int main(){
	time_t a,b;
	while (true) {
		system("SJ.exe > 11input.in");
		
		a = clock();
		system("bc.exe < 11input.in > 12bc.out");
		b = clock(); printf("bc.exe cost %dms\n",b-a); 
		
		a = clock();
		system("me.exe < 11input.in > 12me.out");
		b = clock(); printf("me.exe cost %dms\n",b-a); 
		
		int tmp = system("fc 12bc.out 12me.out > result.txt");
		if (tmp == 1) system("taskkill /F /IM check.exe");
		cout<<endl<<endl;
	}
	return 0;
}

主要是应用了clock()和system()函数
要背住的东西也只有:”taskkill /F /IM check.exe”
运行效果如下:
check_cpp
感觉很不错,用了多少时间一目了然
因为调用了system()所以时间不是特别准,需要减去20ms的样子

【Tricks】关于二进制的黑科技

近日,有幸看到一点黑科技,是一些函数:

— Built-in Function: int __builtin_popcount (unsigned int x)
Returns the number of 1-bits in x.
返回‘1’的个数。

— Built-in Function: int __builtin_ffs (unsigned int x)
Returns one plus the index of the least significant 1-bit of x, or if x is zero, returns zero.
返回右起第一个‘1’的位置。

对于这些函数,他们全是O(1)的
不仅如此,你还会发现你不包含任何库都可以编译通过
而且你会发现,它们也不是宏 QAQ
是不是感觉很神奇?

经过多方查证,这些是GCC编译器提供的函数
换一句话来,这些下划线函数NOI系列赛可以用QAQ
参见:https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
该系列的其他函数,可以在上面的网址全部查到

最后附上据说是 __builtin_popcount 的源码:

int count(unsigned u){
	u = (u & 0x55555555) + ((u >> 1) & 0x55555555);
    u = (u & 0x33333333) + ((u >> 2) & 0x33333333);
    u = (u & 0x0F0F0F0F) + ((u >> 4) & 0x0F0F0F0F);
    u = (u & 0x00FF00FF) + ((u >> 8) & 0x00FF00FF);
    u = (u & 0x0000FFFF) + ((u >> 16) & 0x0000FFFF);
    return u;
}

【算法笔记】非递归DFS

众所周知,DFS可能会爆系统栈。
当然手写栈可以解决这个问题,但是我不会QAQ
但昨天%原教的代码,发现DFS,可以用一个“BFS + while() + 当前弧” 给优雅地替换掉
例如:

void DFS(int w, int f){
	l[w] = ++tot; dep[w] = dep[f]+1;
	for (int i=head[w];i;i=nxt[i])
		if (to[i] != f) DFS(to[i], w);
	r[w] = tot; fa[w][0] = f;
}

就可以用一下代码给完美替代:

inline void BFS(){
	BUF[1] = 1; dep[1] = 1;
	for (int fro=1,bak=1;bak<=fro;bak++){
		int w = BUF[bak];
		for (int i=head[w];i;i=nxt[i]){
			if (!dep[to[i]]) dep[to[i]] = dep[w]+1,
			fa[to[i]][0] = w, BUF[++fro] = to[i];
		}
	}
}

inline void DFS(){
	for (int i=1;i<=n;i++) now[i] = head[i];
	int w = 1; tot = 1;
	while (w) {
		if (!now[w]) {
			r[w] = tot;
			w = fa[w][0];
		} else {
			if (!l[w]) l[w] = ++tot;
			int tmp = to[now[w]]; now[w] = nxt[now[w]];
			if (dep[tmp] != dep[w]+1) continue;
			else w = tmp;
		}
	}
}

代码虽然长一点,但是可以接受,链剖也是可以搞的
至于空间代价,这个只是多了一个当前弧的数组
至于时间代价,有图有真相:non_recursionrecursion

 

【Tricks】手动扩栈

众所周知,HDU等搭建在windows下的OJ系统栈小得一逼,所以我们得手动扩栈……..
其实平时比赛,如果递归爆栈了,也是可以用这个特殊技能水一水的:

G++: 
int size = 256 << 20; // 256MB  
char *p = (char*)malloc(size) + size;  
__asm__("movl %0, %%esp\n" :: "r"(p)); 

C++: 
#pragma comment(linker, "/STACK:102400000,102400000")    

当然,如果会写手工栈,那也是极好的,然而我不会QAQ

———— 2016.8.28 更新 ————-
在BZOJ这种linux平台下,好像上面的扩栈方法不管用
于是我们可以添加如下代码:

const int main_stack=16;
char my_stack[128<<20];//À©Îª128MB
int main()
{
	__asm__("movl %%esp, (%%eax);\n"::"a"(my_stack):"memory");
	__asm__("movl %%eax, %%esp;\n"::"a"(my_stack+sizeof(my_stack)-main_stack):"%esp");
	my_main();
	__asm__("movl (%%eax), %%esp;\n"::"a"(my_stack):"%esp");
	return 0;
}

然后把原来的 int main() 改成 int my_main() 即可

—————————— UPD 2017.7.3 ——————————
在g++的编译命令里加入-Wl,--stack=536870912也可以开栈

【Tricks】STL容器使用指南

1.set.insert() 的返回值是 pair;, 其中bool表示是否插入成功
2.set/map当中的earse() 是完全没有检查机制的(我TM都比他写得好!),使用的时候一定要小心
3.set/map/priority_queue可以仅重载其中的比较符号,例如:

struct CMP{
	bool operator () (const int &a, const int &b){
		return a < b;
	}
};

set<int,CMP> S;
map<int,int,CMP> M;
priority_queue<int,vector<int>,CMP> Q;

4.map使用[]进行访问的时候,即使仅仅是查询、不是赋值,map仍然会新建一个节点
5.sort排序传进去的尾地址不会参与排序