acmer-qualification-code
Programming Contest Archive
Some coding contest solutions are achieved and grouping by hosted platform like Codeforces.
Unaccepted answers are included, be carefull |
# find solutions in Rust
find . -name "*.rs"
# find solutions in C
find . -name "*.c"
Handy templates in Rust
#[allow(unused_imports)]
use std::cmp::max;
#[allow(unused_imports)]
use std::cmp::min;
#[allow(unused_imports)]
use std::collections::HashMap;
#[allow(unused_imports)]
use std::collections::HashSet;
use std::io;
// input macros
#[allow(unused_macros)]
// rustc +nightly -Zunpretty=expanded your.rs
macro_rules! read {
// eg.
// let s = read!();
() => {{
let mut line: String = String::new();
io::stdin().read_line(&mut line).unwrap();
line.trim().to_string()
}};
// eg.
// let v = read!(Vec<i32>)
// let v = read!(Vec<char>)
(Vec<$t:ty>) => ({
let mut line: String = String::new();
io::stdin().read_line(&mut line).unwrap();
line.split_whitespace()
.map(|x| x.parse::<$t>().unwrap())
.collect()
});
// eg.
// let v = read!(i32);
// let v = read!(i64);
// let (i, j, k) = read!(i32, i32, i32);
($($t:ty),*) => {{
let mut line = String::new();
io::stdin().read_line(&mut line).unwrap();
let mut iter = line.split_whitespace();
($(iter.next().unwrap().parse::<$t>().unwrap()),*)
}};
}
fn solve() {
}
fn main() {
let mut t = read!(i32);
while t > 0 {
t -= 1;
solve();
print!("\n");
}
}
Handy templates in C
1. printf
1.1 符号说明
%a(%A) 浮点数、十六进制数字和p-(P-)记数法(C99)
%c 字符
%d 有符号十进制整数
%f 浮点数(包括float和double)
%e(%E) 浮点数指数输出[e-(E-)记数法]
%g(%G) 浮点数不显无意义的零"0"
%i 有符号十进制整数(与%d相同)
%u 无符号十进制整数
%o 八进制整数 e.g. 0123
%x(%X) 十六进制整数0f(0F) e.g. 0x1234
%p 指针
%s 字符串
%% "%"
1.2 对齐
左对齐:"-" e.g. "%-20s"
右对齐:"+" e.g. "%+20s"
空格:若符号为正,则显示空格,负则显示"-" e.g. "% 6.2f"
#:对c,s,d,u类无影响;对o类,在输出时加前缀o;对x类,在输出时加前缀0x;对e,g,f 类当结果有小数时才给出小数点
1.3 格式化输出
[标志][输出最少宽度][.精度][长度]类型
"%-md" :左对齐,若m比实际少时,按实际输出。
"%m.ns":输出m位,取字符串(左起)n位,左补空格,当n>m or m省略时m=n.
e.g. "%7.2s" 输入CHINA
输出" CH"
"%m.nf":输出浮点数,m为宽度,n为小数点右边数位
e.g. "%3.1f" 输入3852.99
输出3853.0
2. 符号的英文读法
+ plus 加号;正号
- minus 减号;负号
± plus or minus 正负号
× is multiplied by 乘号
÷ is divided by 除号
= is equal to 等于号
≠ is not equal to 不等于号
≡ is equivalent to 全等于号
≌ is equal to or approximately equal to 等于或约等于号
≈ is approximately equal to 约等于号
< is less than 小于号
> is greater than 大于号
≮ is not less than 不小于号
≯ is not more than 不大于号
≤ is less than or equal to 小于或等于号
≥ is more than or equal to 大于或等于号
% per cent 百分之…
‰ per mill 千分之…
∞ infinity 无限大号
∝ varies as 与…成比例
√ (square) root 平方根
∵ since; because 因为
∴ hence 所以
∷ equals, as (proportion) 等于,成比例
∠ angle 角
⌒ semicircle 半圆
⊙ circle 圆
○ circumference 圆周
π pi 圆周率
△ triangle 三角形
⊥ perpendicular to 垂直于
∪ union of 并,合集
∩ intersection of 交,通集
∫ the integral of …的积分
∑ (sigma) summation of 总和
° degree 度
′ minute 分
″ second 秒
℃ Celsius system 摄氏度
{ open brace, open curly 左花括号
} close brace, close curly 右花括号
( open parenthesis, open paren 左圆括号
) close parenthesis, close paren 右圆括号
() brakets/ parentheses 括号
[ open bracket 左方括号
] close bracket 右方括号
[] square brackets 方括号
. period, dot 句号,点
| vertical bar, vertical virgule 竖线
& ampersand, and, reference, ref 和,引用
* asterisk, multiply, star, pointer 星号,乘号,星,指针
/ slash, divide, oblique 斜线,斜杠,除号
// slash-slash, comment 双斜线,注释符
# pound 井 号
\ backslash, sometimes escape 反斜线转义符,有时表示转义符或续行符
~ tilde 波浪符
. full stop 句号
, comma 逗号
: colon 冒号
; semicolon 分号
? question mark 问号
! exclamation mark (英式英语) exclamation point (美式英语)
‘ apostrophe 撇号
- hyphen 连字号
– dash 破折号
… dots/ ellipsis 省略号
" single quotation marks 单引号
"" double quotation marks 双引号
‖ parallel 双线号
& ampersand = and
~ swung dash 代字号
§ section; division 分节号
→ arrow 箭号;参见号
3. C/C++头文件
///C
#include <assert.h> //断言
#include <ctype.h> //字符处理
#include <errno.h> //定义错误码
#include <float.h> //浮点数处理
#include <limits.h> //定义各种数据类型最值常量
#include <locale.h> //定义本地化函数
#include <math.h> //定义数学函数
#include <setjmp.h> //非局部跳转
#include <signal.h> //信号处理
#include <stdarg.h> //变长变元表
#include <stddef.h>
#include <stdio.h> //定义输入/输出函数
#include <stdlib.h> //定义杂项函数及内存分配函数
#include <string.h> //字符串处理
#include <time.h> //定义关于时间的函数
///////////////////////////////////////////////////////////////////////////////
///标准 C++
///C语言部分略
#include <algorithm> //STL 通用算法
#include <bitset> //STL 位集容器
#include <complex> //复数类
#include <deque> //STL 双端队列容器
#include <exception> //异常处理类
#include <fstream> //文件输入/输出
#include <functional> //STL 定义运算函数(代替运算符)
#include <limits>
#include <list> //STL 线性列表容器
#include <map> //STL 映射容器
#include <iomanip>
#include <ios> //基本输入/输出支持
#include <iosfwd> //输入/输出系统使用的前置声明
#include <iostream>
#include <istream> //基本输入流
#include <ostream> //基本输出流
#include <queue> //STL 队列容器
#include <set> //STL 集合容器
#include <sstream> //基于字符串的流
#include <stack> //STL 堆栈容器
#include <stdexcept> //标准异常类
#include <streambuf> //底层输入/输出支持
#include <string> //字符串类
#include <utility> //STL 通用模板类
#include <vector> //STL 动态数组容器
#include <cwchar>
#include <cwctype>
using namespace std;
///////////////////////////////////////////////////////////////////////
///C99 增加
#include <complex.h> //复数处理
#include <fenv.h> //浮点环境
#include <inttypes.h> //整数格式转换
#include <stdbool.h> //布尔环境
#include <stdint.h> //整型环境
#include <tgmath.h> //通用类型数学宏
///////////////////////////////////////////////////////////////////////
4. 注意事项
- 变量名count会和<algorithm>中的count冲突
- cin和scanf不能同时使用,cout和printf不能同时使用
- GCC/G++中,输出double应该使用printf("%f") 而不是lf
- 如果用scanf接收了一行数据,再用gets, gets会直接接收到空串!因为,scanf接受了一行数据以后换行符仍然在缓冲区中,gets()遇到换行符,接收会结束. 解决方法,scanf 后加一个getchar()
- 精度问题
- 2^31-1 =0x7fffffff
- -2^31 =-0x7fffffff-1
- 精确的pi :double pi=acos(-1);
5. N次方求和
6. 字符串处理
/**
*颠倒一个字符串的顺序 "abc\0" – "cba\0"
*array 为需要颠倒顺序的字符串
*length 为字符串array的长度
*/
int stringReverse(char *array,int length){
int i = 0,j = length - 1;
while(i < j){
char cha = array[i];
array[i] = array[j];
array[j] = cha;i++;j--;
}
return 1;
}
// 颠倒一个字符串部分位置的顺序, 原地
inline void swap(char *x, char *y) {
char t = *x; *x = *y; *y = t;
}
char* reverse(char *buffer, int i, int j)
{
while (i < j)
swap(&buffer[i++], &buffer[j--]);
return buffer;
}
// 反转整数
#include<stdio.h>
#include<stdlib.h>
#define INF_MAX 0x7fffffff
#define INF_MIN -0x7fffffff-1
int reverse(int x){
int ans = 0;
int l = x;
while(l != 0 ) {
int h = l%10;
l = l/10;
// ans * 10 + h > INF_MAX
// ans * 10 + h < INF_MIN
if(INF_MAX/10 < ans || (INF_MAX/10 == ans && h > 7)) {
return 0;
}else if(INF_MIN/10 > ans || (INF_MIN/10 == ans && h < -8)) {
return 0;
}
ans = ans * 10 + h;
}
return ans;
}
int main() {
printf("%d\n", reverse(123));
printf("%d\n", reverse(-123));
return 0;
}
// 最长回文子串, 暴力解法
// 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
//
// 示例 1:
//
// 输入: "babad"
// 输出: "bab"
// 注意: "aba" 也是一个有效答案。
// 示例 2:
//
// 输入: "cbbd"
// 输出: "bb"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int valid(char *s, int i, int j) {
while(i <= j) {
if(s[i++] != s[j--]) {
return 0;
}
}
return 1;
}
char * longestPalindrome(char * s){
int len = strlen(s);
if (len == 1) {
return s;
}
int max = 1, maxi = 0;
for(int i = 0;i < len-1;i++) {
for(int j = i+1;j < len;j++) {
int x = j - i + 1;
if(x > max && valid(s, i,j)) {
if(x > max) {
max = x;
maxi = i;
}
}
}
}
s = s+maxi;
s[max] = '\0';
return s;
}
int main() {
char a[10] = "babad";
printf("%s\n", longestPalindrome(a));
return 0;
}
7. 进制转换
/**
*将任意进制数转换成整型范围内的十进制数
*str 为base进制数
*base 为str的进制
*length为str的长度
*/
int myPow(int a,int b){
int rs = 1;
while(b--){rs *= a;}
return rs;
}
int anyToDecimal(char *str,int base,int length){
int decimal = 0,i;
for(i = 0;i < length;i++){
if(str[i] >= 65)/*为十六进制的时候*/{
decimal += myPow(base,length-1 - i)*(str[i] - 'A' + 10);
}
else if(str[i] <= '9'){
decimal += myPow(base,length-1 - i)*(str[i] - '0');
}
}
return decimal;
}
8. 栈
/**
*栈 C++
*clear() push() top() empty() pop() size()
*free()
*/
typedef int T;
struct myStack{
int curr,size_limit;
T *array;
myStack(int s){//必须传入栈的大小
curr = -1; size_limit = s;
array = new T[s];
}
void clear(){curr = -1;}/*清空栈 重新使用*/
void free(){delete array;clear();}/*释放栈 不能再使用*/
T top(){
if(curr > -1){return array[curr];}
else return curr;}
bool push(T v){
if(curr+1 < size_limit){array[++curr] = v;return true;}
return false;}
bool empty(){
if(curr < 0)return true;
else return false;}
void pop(){curr--;}
int size(){return curr+1;}
};
// c stack 没有安全检查, 使用时自己注意
#include <stdio.h>
#include <stdlib.h>
typedef int T;
struct stack {
T *values;
int pos, size;
};
void init_stack(struct stack *s, int size) {
s->pos = 0;
s->size = size;
s->values = malloc(sizeof(T) * size);
}
struct stack *create(int size) {
struct stack *p = malloc(sizeof(struct stack));
init_stack(p, size);
return p;
}
void delete (struct stack *s) { free(s->values); }
// not safe
void push(struct stack *s, T v) { s->values[s->pos++] = v; }
void dump(struct stack *s) {
printf("[%p", s->values[0]);
int i = 1;
while(i < s->pos) {
printf(", %p", s->values[i++]);
}
printf("]\n");
}
int empty(struct stack *s) { return s->pos == 0; }
int full(struct stack *s) { return s->pos >= s->size; }
struct T *pop(struct stack *s) { return s->values[--s->pos]; }
struct T *top(struct stack *s) { return s->values[s->pos - 1]; }
int main() {
struct stack *s = create(10);
push(s, 1);
dump(s);
delete (s);
}
8. 表达式转换
/**
*自己定义操作符的优先级
*/
int priorityLevel(const char x){
}
/**
*自己定义操作符的结合性,左或者右
*/
int rightAssociative(const char x){
switch(x){
case '^':{ return 1;}
}
return 0;
}
/**
*shunting_yard: infix expression to RPN
*中缀转换成后缀
*传入中缀表达式expression,RPN保存在result中
*/
void shunting_yard(char *result, const char *expression){
stack<char> my;
int len = strlen(expression), r = 0;
for(int i = 0;i < len;i++){
if(expression[i] == ' ') continue;
if(expression[i] >= '0' && expression[i] <= '9'){
//是数字直接加到输出队列
result[r++] = expression[i];
}
else{//操作符或者括号
///以下代码需要根据具体情况修改
///需要注意操作符的优先级和结合性
if(r != 0 && result[r-1] != ' ') result[r++] = ' ';//数字之间必须要用空格隔开
if(expression[i] == ')'){
while(!my.empty() && my.top() != '('){
//遇到右括号的时候 要将括号里边的表达式看成一个整体
//将左括号之后的内容都弹出
result[r++] = my.top(); my.pop();
}
if(!my.empty()) my.pop();//弹出左括号
}
else{
while(expression[i] != '('//左括号直接入栈
&&!my.empty()//栈为空的时候也直接入栈
///如果是操作符 需要弹出栈里优先级比它高 或者 优先级相等但是是左结合的操作符
&& my.top() != '('//遇到左括号相当于栈为空
&& (
(priorityLevel(my.top()) > priorityLevle(expression[i]))
||(priorityLevel(my.top())==priorityLevle(expression[i])&& !rightAssociative(my.top()))
)
){
result[r++] = my.top(); my.pop();
}
my.push(expression[i]);//将这个操作符入栈
}
}
}
while(!my.empty()){
result[r++] = my.top(); my.pop();
}
result[r] = '\0';
}
9. 搜索
/**
*二分查找
*如果找到flag为1 posi为所查找到的位置
*如果没找到flag为0 posi为该值应该插入的位置
*传入的区间为[left,right)
*/
struct node{
int flag;//0表示不存在
int posi;//返回插入或者删除的位置
};
struct node *binarySearch(int *array,int value,int left,int right){
struct node *pointer = (struct node*)malloc(sizeof(struct node));
pointer->flag = 0;pointer->posi = -1;
if(right == 0){//left == -1 impossible
pointer->flag = pointer->posi = 0;
return pointer;
}
while(left <= right){
int mid = ((left + right) >> 1);
if(value == array[mid]){
pointer->flag = 1;
pointer->posi = mid;
return pointer;
}
else{
(value > array[mid])?(left=mid+1):(right=mid-1);
}
}
pointer->posi = left;return pointer;
}
10. 排序
/**
* 二叉排序树模板
* 插入函数 插入之前应先申请一个要插入的p节点 然后传进去
* 查找函数 root为根节点 parent等于root value为要查找的值
* ret_flag为0返回查找到的节点的父节点这样便于删除时使用 ret_flag
* 为1返回查找到的节点的指针
* 删除函数 传入要删除的节点的父节点和要删除的节点
* 如果待删节点为父节点的左孩子is_left=1 如果为右孩子 is_left=1 构造函数
* 将一个数组从start到end(不包括end)的范围构造为一个二叉排序树 root为根节点
*/
#include <stdio.h>
#include <stdlib.h>
struct node {
int value;
struct node *left, *right;
};
struct node *bst_insert(struct node *curr, struct node *p) {
if (curr == NULL) {
return p; //插入的节点的左右孩子的初始值一定要为NULL
} else if (p->value < curr->value) {
curr->left = bst_insert(curr->left, p);
} else {
curr->right = bst_insert(curr->right, p);
}
return curr;
}
void bst_inorder_traversal(struct node *curr) {
if (curr != NULL) {
bst_inorder_traversal(curr->left);
printf("(%p, %d)\n", curr, curr->value);
bst_inorder_traversal(curr->right);
}
}
void bst_preorder_traversal(struct node *curr) {
if (curr != NULL) {
printf("(%p, %d)\n", curr, curr->value);
bst_preorder_traversal(curr->left);
bst_preorder_traversal(curr->right);
}
}
void bst_postorder_traversal(struct node *curr) {
if (curr != NULL) {
bst_postorder_traversal(curr->left);
bst_postorder_traversal(curr->right);
printf("(%p, %d)\n", curr, curr->value);
}
}
struct node *bst_search(struct node *curr, struct node *parent, int value,
int ret_flag) {
if (curr == NULL) {
// leaf node
return NULL;
} else if (curr->value == value) {
// found, check ret flag
// 返回的是要查找节点的父节点或者是要查找的节点
// 使用父节点是因为这样便于删除的时候使用父节点进行删除
if (ret_flag == 0) {
return parent;
} else {
return curr;
}
} else if (value < curr->value) {
return bst_search(curr->left, curr, value, ret_flag);
} else {
return bst_search(curr->right, curr, value, ret_flag);
}
}
int bst_delete(struct node *parent, struct node *p, int is_left) {
if (p->left == NULL && p->right == NULL) {
// leaft node
if (is_left) {
// p 为parent的左子叶
// 将 parent的左指针置为空
parent->left = NULL;
} else {
// p 为parent的右子叶
parent->right = NULL;
}
free(p);
} else if (p->left == NULL) {
// 待删除节点无左子树,说明有右子树
if (is_left) {
// 将待删除的节点的右子树 赋值给父节点的左子树
parent->left = p->right;
} else {
// 将待删除的节点的右子树 赋值给父节点的左子树
parent->right = p->right;
}
free(p);
} else if (p->right == NULL) {
// 待删除节点无右子树,说明有左子树
if (is_left) {
parent->left = p->left;
} else {
parent->right = p->left;
}
free(p);
} else {
struct node *s = p->right;
struct node *sp = p;
//找右子树中最左的节点 或者找左子树中最右的节点
//然后和要删除的位置交换value
//这里选择前者
while (s->left != NULL) {
sp = s;
s = s->left;
}
printf("%d --> %d\n", p->value, s->value);
p->value = s->value;
if (sp == p) {
// p 的右子树没有左子树
sp->right = s->right;
} else {
//因为s肯定没有左子树 所以把右子树接在父节点的左指针上就行了
sp->left = s->right;
}
free(s);
}
return 1;
}
void bst_build(struct node *root, int *array, int start, int end) {
// [start, end)
while (start < end) {
// create node
struct node *p = malloc(sizeof(struct node));
p->value = array[start];
p->left = p->right = NULL; // init
bst_insert(root, p);
start++;
}
}
int main() {
int a[10] = {1, 450, 3, 4, 56, 12, 123, 45, 23, 6};
struct node *root = malloc(sizeof(struct node));
root->left = root->right = NULL;
root->value = a[0];
bst_build(root, a, 1, 10);
bst_inorder_traversal(root);
printf("root %p %d\n", root, root->value);
struct node *ans = bst_search(root, root, 56, 0);
if (ans != NULL) {
printf("ans value %d\n", ans->value);
bst_delete(ans, ans->right, 1);
bst_preorder_traversal(root);
bst_inorder_traversal(root);
bst_postorder_traversal(root);
}
}
/**
*quickSort , 快速排序,传入要排序的数组和需要排序的范围[left, right]
*从小到大
*/
void quickSort(int left, int right, int array[]){
int i = left,j = right,x = array[(left+right)/2];
do{
while(array[i] < x)i++;
while(array[j] > x)j--;//使左边的值都比右边的小
if(i <= j) {std::swap(array[i++],array[j--]);}
}while(i < j);//i >= j
if(i < right)quickSort(i,right,array);
if(j > left)quickSort(left,j,array);
}
/**
*heapSort 堆排序模板 (大根堆)
*/
int mySwap(int *a,int *b){
if(a == b) return;
(*a) = (*a) ^ (*b);
(*b) = (*a) ^ (*b);
(*a) = (*a) ^ (*b);
return 1;
}
int sift(int array[],int k,int m)//one-based
{
//把k到m范围内的值调整为大根堆
int i = k,j = i<<1;//置i为要筛的节点 j 为i的左孩子
while(j <= m){//仍然在范围之内
if(j < m && array[j] < array[j+1]){//右孩子存在 且左孩子小于右孩子
j++;//因为要建大根堆 所以选择孩子中较大者进行交换
}
if(array[i] > array[j]){
break;//不需要再向下比较 因为之前也是调整过的大根堆
}
else{
mySwap(&array[i],&array[j]);
i = j;j = (i<<1);//被筛节点移动到j节点 计算左孩子
}
}
return 1;
}
int heapSort(int array[],int n){// one-based 从1开始保存的 到n结束 left_child = 2*parent
int i;
for(i = n>>1;i >= 1;i--){
sift(array,i,n);//初始建堆 从 最后一个非终端节点到 1
}
for(i = 1;i < n;i++){//重复执行移走堆顶并重建堆的操作
mySwap(&array[1],&array[n-i+1]);//从n到2 不断和堆顶交换 one-based
sift(array,1,n - i);//重建堆
}
return 1;
}
11. 递归
/**
*汉诺塔 递归实现
*/
int move(int n,char x,char y){
printf("move %d from %c to %c\n",n,x,y);//输出移动过程
return 1;
}
int hanoi(int n,char a,char b,char c){//将n个盘子借助b从a移动到c
if(n==1){move(1,a,c);}
else{
hanoi(n-1,a,c,b);//把n-1个盘子借助c从a移动到b
move(n,a,c);//把第n个盘子从a移动到c
hanoi(n-1,b,a,c);//将n-1个盘子借助a从b移动到c
}
return 1;
}
12. 链表
/**
*构建一个未赋值的循环单链表 至少有头和尾两个节点
*/
struct node{
int value;
struct node *next;
};
struct node *constructRecurrentSingleChain(int start,int end){
struct node *head = (struct node*)malloc(sizeof(struct node));
struct node *tail = (struct node*)malloc(sizeof(struct node));
head->next = tail;
tail->next = head;
//head->value= -1;
//tail->value= -1;
while(start != end){
struct node *new_node = (struct node*)malloc(sizeof(struct node));
//new_node->value = start;根据具体情况进行赋值
new_node->next = head;
tail->next = new_node;
tail = new_node;
start++;
}
return head;
}
/**
*构建未赋值的循环双链表 至少有头和尾两个节点
*/
struct node{
int value;
struct node *prev;
struct node *next;
};
struct node *constructRecurrentDoubleChain(int start,int end){
struct node *head = (struct node*)malloc(sizeof(struct node));
struct node *tail = (struct node*)malloc(sizeof(struct node));
head->prev = tail;
head->next = tail;
tail->next = head;
tail->prev = head;
//head->value= -1;
//tail->value= -1;
while(start != end){
struct node *new_node = (struct node*)malloc(sizeof(struct node));
//new_node->value = start;
tail->next = new_node;
head->prev = new_node;
new_node->prev = tail;
new_node->next = head;
tail = new_node;
start++;
}
return head;
}
13. 大数
/**
*大数的加法 乘法运算
*效率很低 不能用于算大数阶乘
*/
class BigInteger
{
public:
BigInteger();
BigInteger(int);
BigInteger(string);
bool Display();
friend BigInteger operator +(const BigInteger&,const BigInteger&);
friend BigInteger operator *(const BigInteger&,const BigInteger&);
friend ostream& operator<<(std::ostream&,BigInteger&);
private:
static const int max_len = 100000;
public:
int len;
int array[max_len];
};
BigInteger:: BigInteger(){
memset(array,0,sizeof(array));
//或者将sizeof(array) 换成 max_len*4
len = 0;
}
BigInteger:: BigInteger(string digit){
memset(array,0,sizeof(array));
len = digit.size();
for(int i = 0;i < len;i++){
array[i] = digit[len - i - 1] - '0';
}
}
BigInteger:: BigInteger(int digit){
memset(array,0,sizeof(array));
if(digit == 0)len = 1;
else len = 0;
while(digit != 0){
array[len++] = digit % 10;
digit = digit / 10;
}
}
bool BigInteger:: Display(){
for(int i = len - 1;i >= 0;i--) cout << array[i];
return true;
}
std::ostream& operator<<(ostream &out, BigInteger &digit){
digit.Display(); return out;
}
BigInteger operator +(const BigInteger &augend, const BigInteger &addend){
BigInteger result;
result.len = max(augend.len,addend.len);
for(int i = 0;i < result.len;i++)
result.array[i] = augend.array[i] + addend.array[i];
for(int i= 0;i < result.len;i++){
if(result.array[i] >= 10){
result.array[i] -= 10;
result.array[i+1]++;
}
}
if(result.array[result.len] > 0){result.len++;}
return result;
}
BigInteger operator *(const BigInteger &multiplicand, const BigInteger &multiplier)
{
BigInteger result;
result.len = multiplicand.len + multiplier.len - 1;
for(int i = 0;i < multiplicand.len;i++){
for(int j = 0;j < multiplier.len;j++)
result.array[i + j] += multiplicand.array[i]*multiplier.array[j];
}
for(int i = 0;i < result.len;i++){
if(result.array[i] > 9){
result.array[i+1] += result.array[i] / 10;
result.array[i] %= 10;
}
}
if(result.array[result.len] > 0) result.len++;
while(result.len > 1 && result.array[result.len-1] == 0)
result.len--;//为0的时候不用去掉第一位的0
return result;
}
/**
*n的阶乘 结果为大数,保存在array数组中
*end - 1 到 0为结果 (end-1)在前,为高位
*/
int factorial(int *array,int n){
int end = 1; array[0] = 1;
for(int i = 2;i <= n;i++){
int r = 0;
for(int j = 0;j < end;j++){
//乘以每一位
int t = array[j]*i+r;
array[j] = t%10;
r = t/10;
}
while(r){
array[end++] = r % 10;
r /= 10;
}
}
return end;
}
/**
*大数模板(浙大)
*注意这里的int可能会超 void div(bignum_t a,const int b,int& c)
*/
#define DIGIT 3//千进制
#define DEPTH 1000//千进制
#define MAX 1000//位数
typedef int bignum_t[MAX+1];
int read(bignum_t a,istream& is=cin){
char buf[MAX*DIGIT+1],ch;
int i,j;
memset((void*)a,0,sizeof(bignum_t));
if (!(is>>buf)) return 0;
for (a[0]=strlen(buf),i=a[0]/2-1;i>=0;i--)
ch=buf[i],buf[i]=buf[a[0]-1-i],buf[a[0]-1-i]=ch;
for (a[0]=(a[0]+DIGIT-1)/DIGIT,j=strlen(buf);j<a[0]*DIGIT;buf[j++]='0');
for (i=1;i<=a[0];i++)
for (a[i]=0,j=0;j<DIGIT;j++)
a[i]=a[i]*10+buf[i*DIGIT-1-j]-'0';
for (;!a[a[0]]&&a[0]>1;a[0]--);
return 1;
}
void write(const bignum_t a,ostream& os=cout){
int i,j;
for (os<<a[i=a[0]],i--;i;i--)
for (j=DEPTH/10;j;j/=10)
os<<a[i]/j%10;
}
int comp(const bignum_t a,const bignum_t b){
int i;
if (a[0]!=b[0])
return a[0]-b[0];
for (i=a[0];i;i--)
if (a[i]!=b[i])
return a[i]-b[i];
return 0;
}
int comp(const bignum_t a,const int b){
int c[12]={1};
for (c[1]=b;c[c[0]]>=DEPTH;c[c[0]+1]=c[c[0]]/DEPTH,c[c[0]]%=DEPTH,c[0]++);
return comp(a,c);
}
int comp(const bignum_t a,const int c,const int d,const bignum_t b){
int i,t=0,O=-DEPTH*2;
if (b[0]-a[0]<d&&c)
return 1;
for (i=b[0];i>d;i--){
t=t*DEPTH+a[i-d]*c-b[i];
if (t>0) return 1;
if (t<O) return 0;
}
for (i=d;i;i--){
t=t*DEPTH-b[i];
if (t>0) return 1;
if (t<O) return 0;
}
return t>0;
}
void add(bignum_t a,const bignum_t b){
int i;
for (i=1;i<=b[0];i++)
if ((a[i]+=b[i])>=DEPTH)
a[i]-=DEPTH,a[i+1]++;
if (b[0]>=a[0])
a[0]=b[0];
else
for (;a[i]>=DEPTH&&i<a[0];a[i]-=DEPTH,i++,a[i]++);
a[0]+=(a[a[0]+1]>0);
}
void add(bignum_t a,const int b){
int i=1;
for (a[1]+=b;a[i]>=DEPTH&&i<a[0];a[i+1]+=a[i]/DEPTH,a[i]%=DEPTH,i++);
for (;a[a[0]]>=DEPTH;a[a[0]+1]=a[a[0]]/DEPTH,a[a[0]]%=DEPTH,a[0]++);
}
void sub(bignum_t a,const bignum_t b){
int i;
for (i=1;i<=b[0];i++)
if ((a[i]-=b[i])<0)
a[i+1]--,a[i]+=DEPTH;
for (;a[i]<0;a[i]+=DEPTH,i++,a[i]--);
for (;!a[a[0]]&&a[0]>1;a[0]--);
}
void sub(bignum_t a,const int b){
int i=1;
for (a[1]-=b;a[i]<0;a[i+1]+=(a[i]-DEPTH+1)/DEPTH,a[i]-=(a[i]-DEPTH+1)/DEPTH*DEPTH,i++);
for (;!a[a[0]]&&a[0]>1;a[0]--);
}
void sub(bignum_t a,const bignum_t b,const int c,const int d){
int i,O=b[0]+d;
for (i=1+d;i<=O;i++)
if ((a[i]-=b[i-d]*c)<0)
a[i+1]+=(a[i]-DEPTH+1)/DEPTH,a[i]-=(a[i]-DEPTH+1)/DEPTH*DEPTH;
for (;a[i]<0;a[i+1]+=(a[i]-DEPTH+1)/DEPTH,a[i]-=(a[i]-DEPTH+1)/DEPTH*DEPTH,i++);
for (;!a[a[0]]&&a[0]>1;a[0]--);
}
void mul(bignum_t c,const bignum_t a,const bignum_t b){
int i,j;
memset((void*)c,0,sizeof(bignum_t));
for (c[0]=a[0]+b[0]-1,i=1;i<=a[0];i++)
for (j=1;j<=b[0];j++)
if ((c[i+j-1]+=a[i]*b[j])>=DEPTH)
c[i+j]+=c[i+j-1]/DEPTH,c[i+j-1]%=DEPTH;
for (c[0]+=(c[c[0]+1]>0);!c[c[0]]&&c[0]>1;c[0]--);
}
void mul(bignum_t a,const int b){
int i;
for (a[1]*=b,i=2;i<=a[0];i++){
a[i]*=b;
if (a[i-1]>=DEPTH)
a[i]+=a[i-1]/DEPTH,a[i-1]%=DEPTH;
}
for (;a[a[0]]>=DEPTH;a[a[0]+1]=a[a[0]]/DEPTH,a[a[0]]%=DEPTH,a[0]++);
for (;!a[a[0]]&&a[0]>1;a[0]--);
}
void mul(bignum_t b,const bignum_t a,const int c,const int d){
int i;
memset((void*)b,0,sizeof(bignum_t));
for (b[0]=a[0]+d,i=d+1;i<=b[0];i++)
if ((b[i]+=a[i-d]*c)>=DEPTH)
b[i+1]+=b[i]/DEPTH,b[i]%=DEPTH;
for (;b[b[0]+1];b[0]++,b[b[0]+1]=b[b[0]]/DEPTH,b[b[0]]%=DEPTH);
for (;!b[b[0]]&&b[0]>1;b[0]--);
}
void div(bignum_t c,bignum_t a,const bignum_t b){
int h,l,m,i;
memset((void*)c,0,sizeof(bignum_t));
c[0]=(b[0]<a[0]+1)?(a[0]-b[0]+2):1;
for (i=c[0];i;sub(a,b,c[i]=m,i-1),i--)
for (h=DEPTH-1,l=0,m=(h+l+1)>>1;h>l;m=(h+l+1)>>1)
if (comp(b,m,i-1,a)) h=m-1;
else l=m;
for (;!c[c[0]]&&c[0]>1;c[0]--);
c[0]=c[0]>1?c[0]:1;
}
void div(bignum_t a,const int b,long long & c){
int i;
for (c=0,i=a[0];i;c=c*DEPTH+a[i],a[i]=c/b,c%=b,i--);
for (;!a[a[0]]&&a[0]>1;a[0]--);
}
void sqrt(bignum_t b,bignum_t a){
int h,l,m,i;
memset((void*)b,0,sizeof(bignum_t));
for (i=b[0]=(a[0]+1)>>1;i;sub(a,b,m,i-1),b[i]+=m,i--)
for (h=DEPTH-1,l=0,b[i]=m=(h+l+1)>>1;h>l;b[i]=m=(h+l+1)>>1)
if (comp(b,m,i-1,a)) h=m-1;
else l=m;
for (;!b[b[0]]&&b[0]>1;b[0]--);
for (i=1;i<=b[0];b[i++]>>=1);
}
int length(const bignum_t a){
int t,ret;
for (ret=(a[0]-1)*DIGIT,t=a[a[0]];t;t/=10,ret++);
return ret>0?ret:1;
}
int digit(const bignum_t a,const int b){
int i,ret;
for (ret=a[(b-1)/DIGIT+1],i=(b-1)%DIGIT;i;ret/=10,i--);
return ret%10;
}
int zeronum(const bignum_t a){
int ret,t;
for (ret=0;!a[ret+1];ret++);
for (t=a[ret+1],ret*=DIGIT;!(t%10);t/=10,ret++);
return ret;
}
void comp(int* a,const int l,const int h,const int d){
int i,j,t;
for (i=l;i<=h;i++)
for (t=i,j=2;t>1;j++)
while (!(t%j))
a[j]+=d,t/=j;
}
void convert(int* a,const int h,bignum_t b){
int i,j,t=1;
memset(b,0,sizeof(bignum_t));
for (b[0]=b[1]=1,i=2;i<=h;i++)
if (a[i])
for (j=a[i];j;t*=i,j--)
if (t*i>DEPTH)
mul(b,t),t=1;
mul(b,t);
}
void combination(bignum_t a,int m,int n){
int* t=new int[m+1];
memset((void*)t,0,sizeof(int)*(m+1));
comp(t,n+1,m,1);
comp(t,2,m-n,-1);
convert(t,m,a);
delete []t;
}
void permutation(bignum_t a,int m,int n){
int i,t=1;
memset(a,0,sizeof(bignum_t));
a[0]=a[1]=1;
for (i=m-n+1;i<=m;t*=i++)
if (t*i>DEPTH)
mul(a,t),t=1;
mul(a,t);
}
14. 并查集
int father[15];
void makeSet(){
for(inti = 0;i <= n;i++){
father[i] = i;
}
}
int findSet(int x){
if(x != father[x]){
father[x] = findSet(father[x]);
}
return father[x];
}
void unionSet(int x,int y){
x = findSet(x);
y = findSet(y);
father[x] = y;
}
15. 状态压缩
/**
*状态压缩法求阶乘,虽然可以求但这只是状态压缩恰好的一个性质 它主要用在动态规划中
*注释里的是f[13] = f[12] + f[5] + f[9] 的工作过程,t -= t & -t 就是将最开始的t的每位1取出来,取
*出来的那位1和原来的值t异或就把那位1变成0了,有点别扭 :(
*/
long long f[1<<20] = {};
f[0] = 1;//f[2^0 - 1] = f[0] = 1 (0的阶乘为1)
for(int i = 1;i < (1<<n);i++){// 1 到 2^n - 1
for(int t = i; t > 0; t -= (t & -t)){
//f(01101) = f(00101) + f(01001) + f(01100)
//1 t = 13
//2 13 -= 1
//3 12 -= 4
//4 8 -= 8
f[i] += f[i ^ (t & -t)];//将某些位变为0 计算和
//1 f[(01101) ^ (01101 & 10011)] = f[01100]
//2 f[(01101) ^ (01100 & 10100)] = f[01001]
//3 f[(01101) ^ (01000 & 11000)] = f[00101]
}
}
// n! = f[(1<<n)-1]
16. 树状数组
/**
*一维树状数组,树状数组C[],输入的数组A[]
*A[] C[]都是从1开始的 在build之前C[]初始化为0
*/
int lowbit(int x){
return x & (x^(x-1));//这一步是把x的二进制表示中的最低位1取出来
//x = ***100
//x-1 = ***011
//x ^ (x-1) = 000111
//x & (x ^ (x-1)) = 000100
//return x&(-x);//另一种写法
//x = ***100
//-x = ---011 + 1 = ---100;
//x&-x = 000100;
}
void add(int i,int v,int upper){//对A[i]进行加v操作 同时更新C
//对A[i]的更新直接不要放在这里 因为build也要调用该函数
while(i <= upper){
C[i] += v; i += lowbit(i);
}
}
void build(int begin,int end){//根据A[begin]到A[end]构建树状数组
while(begin <= end){
add(begin,A[begin],end);
}
}
int sum(int i){//求前n项和
int sum = 0;
while(i >= 1){
sum += C[i]; i -= lowbit(i);
}
return sum;
}
/**
*二维树状数组
*二维树状数组和一维几乎一样
*/
int lowbit(int x){
return x & (x^(x-1));
}
void add(int i,int j,int v,int upper){//增加
int t_i,t_j;
for(t_i = i;t_i <= upper;t_i += lowbit(t_i)){
for(t_j = j;t_j <= upper;t_j += lowbit(t_j)){
C[t_i][t_j] += v;
}
}
}
int query(int i,int j){//查询
int sum = 0,t_i,t_j;
for(t_i = i;t_i > 0;t_i -= lowbit(t_i)){
for(t_j = j;t_j > 0;t_j -= lowbit(t_j)){
sum += C[t_i][t_j];
}
}
return sum;
}
17. 线段树
/**
*树状数组是前序和,应用范围要窄,线段树可以求区间和、区间最大值等等
*下面是求线段树求区间和的示例,可以根据具体情况修改
*/
#define MAX 100000
struct node{
int left,right;
long long sum,weight;
}tree[MAX];
int A[MAX];//MAX_n
void build(int i,int a,int b){//构建线段树 i为层数 初始为1 ,传入区间[a,b]
//tree[i].left tree[i].right为节点i的左右边界
if(a != b){
tree[i].left = a;tree[i].right= b;
build(i<<1,a,(a+b)>>1);//递归构造左子树
build((i<<1)+1,((a+b)>>1)+1,b);//递归构造右子树
tree[i].sum = tree[i<<1].sum + tree[(i<<1)+1].sum;//回溯 左右子树的和等于父节点
}
else{
tree[i].left = tree[i].right = a;
tree[i].sum = A[a];//A[]为输入的数组
return;
}
tree[i].weight = 0;
}
int add(int i,int a,int b,int v){//将某个区间的所有值加上v
if(tree[i].left == a && tree[i].right == b){
//恰好是这个区间
tree[i].weight += v;//将这个值保存而不是直接累加
//查询的时候再进行更新 节省时间开销
return 1;
}
else{
//不是该区间但是属于该区间
tree[i].sum += v*(b - a + 1);
}
int mid = (tree[i].left+tree[i].right) >> 1;
if(a <= mid && b > mid){
add(i<<1,a,mid,v);
add((i<<1)+1,mid+1,b,v);
}
else{
add((i<<1)+(b<=mid?0:1),a,b,v);
}
return 1;
}
long long query(int i,int a,int b){//查询某个区间的所有值的和
int mid = (tree[i].left+tree[i].right) >> 1;
if(tree[i].left == a && tree[i].right == b){
return tree[i].sum + (tree[i].right - tree[i].left + 1)*tree[i].weight;
}
else if(tree[i].weight != 0){
tree[i].sum += (tree[i].right - tree[i].left + 1)*tree[i].weight;//首先更新自己
tree[i<<1].weight += tree[i].weight;//然后传递给左右子树
tree[(i<<1)+1].weight += tree[i].weight;
tree[i].weight = 0;//自身清零
}
if(a <= mid && b > mid){
return query(i<<1,a,mid) + query((i<<1)+1,mid+1,b);
}
return query((i<<1) + (b<=mid?0:1),a,b);
}
/**
*二维线段树
*调用add,query,build时注意传参时的大小顺序,左小于右
*二维线段树的x维度和y维度表示的意义不一定是一样的
*下面是求平面上的矩形面积和(去掉重复区域后的面积和),可以根据具体情况修改
*/
#define MAX 1500
vector<double> x;
vector<double> y;//用于离散化
struct y_node{
int left,right;
double len;//被覆盖的区间长度
};
struct x_node{
int left,right;
double area;//被覆盖的面积
struct y_node sub[MAX];
}tree[MAX];
void buildSub(int i,int j,int yl,int yr){
tree[i].sub[j].left = yl;
tree[i].sub[j].right = yr;
tree[i].sub[j].len = 0;
if(yr - yl == 1){
return;
}
buildSub(i,j<<1,yl,(yl+yr)>>1);
buildSub(i,(j<<1)+1,((yl+yr)>>1),yr);//最小元是个线段所以不加1
}
void build(int i,int xl,int xr,int ylm,int yrm){
tree[i].left = xl;tree[i].right= xr;tree[i].area = 0;
if(xr - xl == 1){
buildSub(i,1,ylm,yrm);//建立子树 只对叶子节点建立子树
return;
}
build(i<<1,xl,(xl+xr)>>1,ylm,yrm);
build((i<<1)+1,((xl+xr)>>1),xr,ylm,yrm);
}
void addSub(int i,int j,int yl,int yr){
if(tree[i].sub[j].left == yl && tree[i].sub[j].right == yr){
tree[i].sub[j].len = y[tree[i].sub[j].right] - y[tree[i].sub[j].left];
return;
}
int mid = (tree[i].sub[j].left + tree[i].sub[j].right)>>1;
if(yl < mid && yr > mid){
addSub(i,(j<<1),yl,mid);
addSub(i,(j<<1)+1,mid,yr);
}
else{
addSub(i,(j<<1)+((yl >= mid)?1:0),yl,yr);
}
tree[i].sub[j].len = mymax(tree[i].sub[j].len,tree[i].sub[(j<<1)].len + tree[i].sub[(j<<1)+1].len);
}
void add(int i,int xl,int xr,int yl,int yr){//
if(tree[i].right - tree[i].left == 1){//元区间
addSub(i,1,yl,yr);//对y坐标加边 并计算长度
tree[i].area = tree[i].sub[1].len*(x[xr]-x[xl]);//计算面积
return;
}
int mid = (tree[i].left + tree[i].right)>>1;
if(xl < mid && xr > mid){
add((i<<1),xl,mid,yl,yr);
add((i<<1)+1,mid,xr,yl,yr);
}
else{
add((i<<1)+((xl >= mid)?1:0),xl,xr,yl,yr);
}
//更新面积
tree[i].area = tree[(i<<1)].area + tree[(i<<1)+1].area;
}
double querySub(int i,int j,int ya,int yb){
if(tree[i].sub[j].left == ya && tree[i].sub[j].right == yb){
return tree[i].sub[j].len;
}
else{
//不是目标区间但包含目标区间
//do something
}
int mid = (ya+yb)>>1;
if(ya <= mid && yb > mid){
return querySub(i,j<<1,ya,mid) + querySub(i,(j<<1)+1,mid,yb);
}
else return querySub(i,(j<<1)+(yb<=mid?0:1),ya,yb);
}
double query(int i,int xa,int xb,int ya,int yb){
if(tree[i].left == xa && tree[i].right == xb){
return tree[i].area;
}
else{
//do something
}
int mid = (xa+xb)>>1;
if(xa <= mid && xb > mid){
return query(i<<1,xa,mid,ya,yb) + query((i<<1)+1,mid,xb,ya,yb);
}
else return query((i<<1)+(xb<=mid?0:1),xa,xb,ya,yb);
}
//main中的离散化代码
sort(x.begin(),x.end());
sort(y.begin(),y.end());//排序之后去重
x.resize(unique(x.begin(),x.end()) - x.begin());
y.resize(unique(y.begin(),y.end()) - y.begin());
18. Trie树
/**
*trie树,hash的方式,用空间换时间 注意别忘了创建根节点
*/
#define MAX 256 //每个节点可能有多少个孩子
struct node{
///数据域
int used;//标记是否有对应的字母
struct node *next[MAX];
};
void create(struct node *t){
for(int i = 0;i < MAX;i++){
t->next[i] = new struct node;
///initialize
}
}
void trieInsert(struct node *curr,const char *str,const int len){
for(int i = 0;i < len;i++){
//在当前节点的孩子中找
int index = (int)str[i];
if(curr->next[index]->used == 0){//没有这个字母
curr->next[index]->used = 1;
create(curr->next[index]);//创建它的孩子
}
curr = curr->next[index];
}
///do something
}
19. 矩阵
/**
*矩阵快速幂(2行2列)
*/
typedef double dataType;
struct matrix{
dataType mat[2][2];
matrix(){memset(mat,0,sizeof(mat));}//初始化为0
matrix(double v1,double v2,double v3,double v4){mat[0][0] = v1;mat[0][1] = v2;mat[1][0] = v3;mat[1][1]=v4;}
matrix operator*(const matrix &b){
matrix rs;
for(int i = 0;i < 2;i++){
for(int k = 0;k < 2;k++){
for(int j = 0;j < 2;j++){
rs.mat[i][j] += mat[i][k]*b.mat[k][j];
}
}
}
return rs;
}
};
matrix fastPower(matrix a,int po){
//a^po
matrix rs(1,0,0,1);//初始化为1
while(po){
if(po&1){rs = rs*a;}
a = a*a;po = (po>>1);
}
return rs;
}
20. 精度处理
/**
*返回0表示x==0,-1表示x < 0, 1表示x大于0
*complf(a-b) == 0, a == b 或者 fabs(a-b) < eps
*complf(a-b) != 0, a != b 或者 fabs(a-b) > eps
*complf(a-b) < 0, a < b 或者 a - b < -eps
*complf(a-b) > 0, a > b 或者 a - b > eps
*complf(a-b) <= 0, a <= b 或者 a - b < eps
*complf(a-b) >= 0, a >= b 或者 a - b > -eps
*/
#define eps 1e-8
int complf(double x){ return x < -eps?-1:((x < eps)?0:1);}
21. 动态规划
/**
*0-1 背包 (二维实现,可以优化到一维)
*注释中所说的对象可以为一个物体,也可以为一种方案,视题目而定
*/
dp[n+1][v+1];
memset(dp[0],0,sizeof(int)*(v+1));//不放任何对象时,不管背包容量多少的价值都是0
for(i = 1;i <= n;i++){//从第一个对象开始
//处理第i 个对象,在当前背包容量为j的时候选还是不选这个对象
for(j = 0;j <= v;j++){//枚举每个容量
if(volume[i] > j){//这个对象肯定是不能放在容量为j的背包里的
//如果j < volume[i],相减之后就小于0了
dp[i][j] = dp[i-1][j];
}
else{//如果体积刚好等于剩余的容量也不一定会被放进去
//因为可能有其它几个对象组合之后的价值比这个单个对象的高
dp[i][j] = mymax(dp[i-1][j],dp[i-1][j-volume[i]]+weight[i]);
}
}
}//结果保存在 dp[n][v]中,即对前n个对象做出取舍后的最优解
22. 素数生成
/**
*筛法求素数 筛法求素数,找到[1,MAXL]的所有素数
*/
#define MAXL 100000
#define MAXC 50000
bool not_prime[MAXL+1];//用于判断是否被筛掉 2的倍数都会被筛掉,但是没有标记
int primeTable[MAXC];//保存素数
long long getPrime(){
long long i,j,step,prime_num = 1;
not_prime[0] = not_prime[1] = true;
primeTable[0] = 2;//第一个素数是2
for(i = 3;i <= MAXL;i+=2){
if(not_prime[i] == false){
primeTable[prime_num++] = i;
for(j = i*i,step = 2*i;j <= MAXL;j += step){not_prime[j] = true;}
}
}
return prime_num;
}
23. 素数测试
/**
*Miller-Rabin 素数测试
*随机选取s个基 出错的概率至多为 1/(2^s),50已经足够了
*RAND_MAX包含在stdlib中,不同的库会有不同,但都至少为32767
*/
/**
*快速幂取模 返回 a^n mod m
*/
long long exp_mod(long long a,long long n,long long m){
//x*y mod m = ((x%m) * (y%m))%m
if(n == 1)
return a % m;
else if(n == 0)
return 1;
if(n&1){//奇数 a^n = (a^(n-1))*a
return ((a%m)*exp_mod(a,n-1,m)) % m;
}
else{//偶数 a^n = (a^(n>>1))^2
long long t = exp_mod(a,n>>1,m) % m;
return t*t%m;
}
}
int witness(int a,int n){
//a^(n-1) = 1 (mod n)
int t = 0,u = n-1;
while(!(u&1)){t++; u = (u>>1);}// n - 1 = u*(2^t)
long long x[2]; x[0] = x[1] = exp_mod(a,u,n);
for(int i = 1;i <= t;i++){
x[1] = x[0]*x[0]%n;
if(x[1] == 1 && x[0] != 1 && x[0] != (n-1)){
return 1;//检测到一个非平凡平方根
}
x[0] = x[1];
}
if(x[1] != 1) return 1;
return 0;
}
int millerRabin(int n,int s = 50){
if(n == 2) return 1;
else if(!(n&1)) return 0;
for(int i = 0;i < s;i++){
int a = (int)((rand()*1.0/RAND_MAX)*(n-2)) + 1;
if(witness(a,n)){
//a^(n-1) = 1 (mod n) 如果n为素数,那么对于a(1<=a<=n-1)都满足这个等式
return 0;//基数a是n为合数的证据
}
}
return 1;//几乎可以确定n是个素数
}
24. 最大公约数/最小公倍数
/**
*最大公约数 gcd
*最小公倍数 lcm = a*b/gcd(a,b)
*/
int gcd(int a,int b){
return b ? gcd( b,a % b ) : a;
}
int lcm(int a,int b){
return a/gcd(a,b)*b;
}
/**
*二进制欧几里得辗转相除法求gcd
*传参的时候注意a >= b
*/
typedef long long int64;
int64 binaryGcd(int64 a,int64 b){
if(a == b) return a;
if((a&1) && (b&1)){
return binaryGcd(a-b,b);
}
else if((a&1) == 0 && (b&1) == 0){
return 2*binaryGcd(a>>1,b>>1);
}
else if((a&1)){
return binaryGcd(a,b>>1);
}
return binaryGcd(mymax(a>>1,b),mymin(a>>1,b));
}
25. 欧拉 phi 函数
/**
*欧拉phi函数 返回小于x且与x互质的数的个数
*/
int euler_phi(int x){
int a = ceill(sqrt(x)),i,rs = x;
for(i = 2;i <= a;i++){
if(x % i == 0){
rs -= rs/i;//减去 在这rs个元素中有i因子的 数 的个数
while(x % i == 0){x /= i;}//将x中含有i因子的数去掉
if(x == 1)break;//x已经到1了
}
}
if(x != 1) {rs -= (rs / x);}
return rs;
}
26. 快速幂取模
/**
*快速幂取模 返回 a^n mod m
*/
int exp_mod(int a,int n,int m){
//x*y mod m = ((x%m) * (y%m))%m
if(n == 1)
return a % m;
else if(n == 0)
return 1 % m;
if(n&1){//奇数 a^n = (a^(n-1))*a
return ((a%m)*exp_mod(a,n-1,m)) % m;
}
else{//偶数 a^n = (a^(n>>1))^2
long long t = exp_mod(a,n>>1,m) % m;
return t*t%m;
}
}
27. 扩展欧几里得
/**
*扩展欧几里德 ax+by = gcd(a,b) 解出x,y
*/
long long extendedEuclid(long long a,long long b,long long &x,long long &y){
if(b == 0){
x = 1; y = 0; return a;
}
else{
long long gcd,x1,y1;
gcd = extendedEuclid(b,a%b,x1,y1);
x = y1; y = x1 - (a/b)*y1;
return gcd;
}
}
28. 梅森素数
/**
*扩展欧几里德 ax+by = gcd(a,b) 解出x,y
*/
long long extendedEuclid(long long a,long long b,long long &x,long long &y){
if(b == 0){
x = 1; y = 0; return a;
}
else{
long long gcd,x1,y1;
gcd = extenedEuclid(b,a%b,x1,y1);
x = y1; y = x1 - (a/b)*y1;
return gcd;
}
}
29. 最大流
/**
*最大流 传入源点 汇点和顶点数
*graph[u][v]为u到v的剩余流量 (residual flow)
*初始的流量根据具体情况而定,如果没有边相连流量为0
*graph[v][u]为流过e(u,v)的流量
*/
#define INF 1000000000
#define MAX 100
int Edmonds_Karp(int source, int sink, int vertex_num){
int maxFlow = 0;
while(true) {
int head, tail, pre_of[MAX], my_queue[MAX];
head = tail = 0;//初始化队列
my_queue[tail++] = source;//放入源点
memset(pre_of, -1, sizeof(pre_of));////(pre_of[v],v) 代表边(u,v)
while(head < tail){
int u = my_queue[head++];
for(int v = 1; v <= vertex_num; v ++) {//也可以从0开始
if(pre_of[v] == -1 && graph[u][v] > 0){
my_queue[tail++] = v;/*入队*/
pre_of[v] = u;/*保存这条边*/
if(u == sink) break;
}
}
if(pre_of[sink] != -1) break; //找到增广路径
}
if(pre_of[sink] == -1) break;//没有增广路径
int min_flow = INF;
for(int v = sink; v != source; v = pre_of[v]) {
min_flow = mymin(min_flow, graph[pre_of[v]][v]);
//对pre保存的路径进行回溯找到最小的流(最大可流通流量)
}
maxFlow += min_flow;
for(int v = sink; v != source; v = pre_of[v]) {//更新流网络
graph[pre_of[v]][v] -= min_flow;
graph[v][pre_of[v]] += min_flow;
}
}
return maxFlow;
}
/**
*ISAP求最大流
*/
typedef struct {
int v, next, val;
} edge;
const int MAXN = 20010;
const int MAXM = 500010;
edge e[MAXM];
int p[MAXN], eid;
inline void init() {
memset(p, -1, sizeof(p));
eid = 0;
}
//有向
inline void insert1(int from, int to, int val) {
e[eid].v = to;
e[eid].val = val;
e[eid].next = p[from];
p[from] = eid++;
swap(from, to);
e[eid].v = to;
e[eid].val = 0;
e[eid].next = p[from];
p[from] = eid++;
}
//无向
inline void insert2(int from, int to, int val) {
e[eid].v = to;
e[eid].val = val;
e[eid].next = p[from];
p[from] = eid++;
swap(from, to);
e[eid].v = to;
e[eid].val = val;
e[eid].next = p[from];
p[from] = eid++;
}
int n, m; //n为点数 m为边数
int h[MAXN];
int gap[MAXN];
int source, sink;
inline int dfs(int pos, int cost) {
if (pos == sink) {
return cost;
}
int j, minh = n - 1, lv = cost, d;
for (j = p[pos]; j != -1; j = e[j].next) {
int v = e[j].v, val = e[j].val;
if(val > 0) {
if (h[v] + 1 == h[pos]) {
if (lv < e[j].val) {
d = lv;
} else {
d = e[j].val;
}
d = dfs(v, d);
e[j].val -= d;
e[j ^ 1].val += d;
lv -= d;
if (h[source] >= n) {
return cost - lv;
}
if (lv == 0) {
break;
}
}
if (h[v] < minh) {
minh = h[v];
}
}
}
if (lv == cost) {
--gap[h[pos]];
if (gap[h[pos]] == 0) {
h[source] = n;
}
h[pos] = minh + 1;
++gap[h[pos]];
}
return cost - lv;
}
int sap(int st, int ed) {
source = st;
sink = ed;
int ret = 0;
memset(gap, 0, sizeof(gap));
memset(h, 0, sizeof(h));
//gap[st] = n;
gap[0] = n;
while (h[st] < n) {
ret += dfs(st, INT_MAX);
}
return ret;
}
30. 最短路
/**
*SPFA可以用来求单源最短路径和求解差分约束
*可以处理负边和负权回路
*/
#define INF 1000000000
#define MAX 50010//最大的点数,根据题目
struct node{int u,v,weight,next;}edge[10*MAX];//边数一般比点数多
int head[MAX];int count = 0;
void add(int u,int v,int c){//邻接表的加边操作
edge[count].u = u;edge[count].v = v;edge[count].weight = c;
edge[count].next = head[u];head[u] = count++;
}
int SPFA(int ll,int rr){
//根据具体情况对i点到起点的长度进行初始化
int d[MAX];for(int i = ll;i <= rr;i++){d[i] = -INF;}d[ll] = 0;
queue<int> my_queue;my_queue.push(ll);//放入起点
bool in_queue[MAX] = {};//用于判断某点当前是否在队列中
while(!my_queue.empty()){
int start = my_queue.front();my_queue.pop();
in_queue[start] = false;//拿出来了 所以不在队列中了
for(int i = head[start]; i != -1; i = edge[i].next){
int u = edge[i].u,v = edge[i].v,weight_of_uv = edge[i].weight;
if(d[v] < d[u] + weight_of_uv){
d[v] = d[u] + weight_of_uv;
if(!in_queue[v]){
//可以入队且不在队列中
//可能会继续松弛表示可以入队
my_queue.push(v);
in_queue[v] = true;//标记为在队列中
}
}
}
}
return d[rr];//根据具体情况返回一个结果
}
/**
*dijkstra求最短路,用优先队列优化
*传入源点和顶点个数(注意顶点是从0还是1开始)
*如果只计算源点到单个目的点的最短路,需将flag标记为1 并传入目的点
*注意head, countt等初始化
*/
#define MAX 1100
#define INF 1000000000
struct node{
int u,v,w,next;//顶点结构体
}edge[100100];
int head[MAX], countt=0; //每次都要初始化
void add(int u, int v, int w){//加边
edge[countt].u = u;
edge[countt].v = v;
edge[countt].w = w;
edge[countt].next = head[u];head[u] = countt++;
}
struct node2{
int ver,dist; //顶点和dist[ver]
node2(int v,int d){ver = v;dist = d;}
};
bool operator > (const node2 &a, const node2 &b){
//重载优先队列的 > 运算符
if(a.dist > b.dist)return true;
return false;
}
bool operator < (const node2 &a, const node2 &b){
//重载优先队列的 < 运算符
if(a.dist < b.dist)return true;
return false;
}
int dijkstra(int source, int vertex_num, int end=-1, int flag=0){
int dist[MAX];
//优先队列(小根,top为最小值)
priority_queue<node2,vector<node2>,greater<node2> > my;
for(int i = 1;i <= vertex_num;i++){
dist[i] = INF;
}dist[source] = 0;
my.push(node2(source,0));//放入源点
int pre_of[MAX]; memset(pre_of,-1,sizeof(pre_of));
while(!my.empty()){
node2 u = my.top();my.pop();
if(flag && u.ver == end){return dist[u.ver];}
if(dist[u.ver] == INF) break;//肯定没有更小的
for(int i = head[u.ver];i != -1;i = edge[i].next){
int v = edge[i].v,w = edge[i].w;
if(dist[v] > dist[u.ver] + w){
dist[v] = dist[u.ver] + w;
pre_of[v] = u.ver;//保存路径
my.push(node2(v,dist[v]));
}
}
}
///dist[i]保存的是源点到所有i点的最短距离
return 1;
}
31. 最小生成树
/**
*kruskal求最小生成树
*/
#define MAX 1000
struct node{int u,v,cost;}array[MAX*MAX];
int edge_count;//边的数量
int father[MAX];
void makeSet(int n){
for(int i = 1;i <= n;i++){
//根据具体条件对并查集初始化
father[i] = i;
}
}
int find(int x){
if(father[x] != x){
return father[x] = find(father[x]);
}
return father[x];
}
void unionSet(int x,int y){
//将x所在的集合合并到y所在的集合
father[find(x)] = find(y);
}
int kruskal(){
int total_cost = 0;//总花费
//对边排序
pseudocode: sort(array,array+edge_count);
for(int i = 0;i < edge_count;i++){
if(find(array[i].u)==find(array[i].v)){
//端点在同一个集合的不能添加进去
//因为会构成回路
continue;
}
total_cost += array[i].cost;
unionSet(array[i].u,array[i].v);
}
return total_cost;
}
31. 有向图的强连通分量
/**
*Tarjan algorithm for strongly connected component
*求强连通分量的tarjan算法,邻接表表示
*注意数组的初始化
*/
int dfs_order[MAX], lowest[MAX];
int my_stack[MAX],visited[MAX],in_stack[MAX];
int mark_num,top;
struct node{
int u,v,next;
}edge[MAX*5];
void tarjan_scc(int u){
dfs_order[u] = lowest[u] = mark_num++;
my_stack[top++] = u;visited[u] = 1;in_stack[u] = 1;
for(int i = head[u];i != -1;i = edge[i].next){
int v = edge[i].v;
if(!visited[v]){
tarjan_scc(v);
lowest[u] = mymin(lowest[u],lowest[v]);
}
else if(in_stack[v]){
lowest[u] = mymin(lowest[u],dfs_order[v]);
}
}
if(dfs_order[u] == lowest[u]){
int p;
do{
p = my_stack[--top];in_stack[p] = 0;
///这里出栈的是当前的一个强连通分量
///根据具体情况处理
}while(u != p);
}
}
32. 无向图的双连通分量
/**
*Tarjan algorithm for strongly connected component
*求强连通分量的tarjan算法,邻接表表示
*注意数组的初始化
*/
int dfs_order[MAX], lowest[MAX];
int my_stack[MAX],visited[MAX],in_stack[MAX];
int mark_num,top;
struct node{
int u,v,next;
}edge[MAX*5];
void tarjan_scc(int u){
dfs_order[u] = lowest[u] = mark_num++;
my_stack[top++] = u;visited[u] = 1;in_stack[u] = 1;
for(int i = head[u];i != -1;i = edge[i].next){
int v = edge[i].v;
if(!visited[v]){
tarjan_scc(v);
lowest[u] = mymin(lowest[u],lowest[v]);
}
else if(in_stack[v]){
lowest[u] = mymin(lowest[u],dfs_order[v]);
}
}
if(dfs_order[u] == lowest[u]){
int p;
do{
p = my_stack[--top];in_stack[p] = 0;
///这里出栈的是当前的一个强连通分量
///根据具体情况处理
}while(u != p);
}
}
33. 二分图的最大匹配
/**
*二分图的最大匹配(邻接矩阵),交大模板
*graph初始化为0,返回最大匹配数
*注意要给nx ny赋值
*/
#define MAX 510
int nx,ny,graph[MAX][MAX],sy[MAX],cx[MAX],cy[MAX];
int path(int u){
for(int v = 1; v <= ny;v++){
if(graph[u][v] && !sy[v]){
sy[v] = 1;
if(!cy[v] || path(cy[v])){
cx[u] = v; cy[v] = u;
return 1;
}
}
}
return 0;
}
int maximumMatch(){
int i,ret = 0;
memset(cx,0,sizeof(cx));
memset(cy,0,sizeof(cy));
for(i = 1;i <= nx;i++){
if(!cx[i]){//
memset(sy,0,sizeof(sy));
ret += path(i);
}
}
return ret;
}
34. 叉积/点与线段/线段与线段
struct point{double x,y;};
struct segment{point a,b;};
/**
*cross product
*(p2-p1) X (q2-q1) -> aXb
*(p2.x-p1.x,p2.y-p1.y) X (q2.x-q1.x,q2.y - q1.y)
*结果大于0说明 b到a为顺时针 等于0说明a b共线 小于0说明b到a为逆时针
*/
double crossProduct(const point &p1, const point &p2,const point &q1,const point &q2){
return (p2.x-p1.x)*(q2.y-q1.y) - (p2.y-p1.y)*(q2.x-q1.x);
}
/**
*判断点p是否在线段(q1,q2)上
*判断点p是否在线段s上
*/
int onSegment(const point &p, const point &q1, const point &q2){
double rs = crossProduct(q1,p,q2,p);
if(complf(rs) == 0){
//共线
if(p.x <= mymax(q1.x,q2.x) && p.x >= mymin(q1.x,q2.x)
&& p.y <= mymax(q1.y,q2.y) && p.y >= mymin(q1.y,q2.y)){
return 1;
}
}
return 0;
}
int onSegment(const point &p,const segment &s){
double rs = crossProduct(s.a,p,s.b,p);
if(rs >= 0 && rs < eps){
if(p.x <= mymax(s.a.x,s.b.x) && p.x >= mymin(s.a.x,s.b.x)
&& p.y <= mymax(s.a.y,s.b.y) && p.y >= mymin(s.a.y,s.b.y)){
return 1;
}
}
return 0;
}
/**
*判断两线段是否相交
*/
int intersect(const segment &s1, const segment &s2){
//先判断一条线段的端点是否在另外一条线段上
if(onSegment(s1.a,s2)
|| onSegment(s1.b,s2)
|| onSegment(s2.a,s1)
|| onSegment(s2.b,s1)){
//
return 1;
}
//再判断线段的两个端点是否在另外一条线段的两侧
if(crossProduct(s1.a,s1.b,s1.a,s2.a)*crossProduct(s1.a,s1.b,s1.a,s2.b) < 0
&& crossProduct(s2.a,s2.b,s2.a,s1.a)*crossProduct(s2.a,s2.b,s2.a,s1.b) < 0){
return 1;
}
return 0;
35. 组合
/**
*组合 从a个数中选b个的选法C(a,b)
*/
long long combination( long long a,long long b )
{
long long i,sum = 1;
if( a-b < b ) b = a-b;
for( i = 0; i < b; i++ ){
sum *= ( a - i ); sum/=i+1;
}
return sum;
}
36. Catalan Number
/**
*网上找的模板 验证过前一百的catalan数
*/
#include<iostream>
#include<string>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 9999
#define DLEN 4
class BigNum{
private:
int a[300];//DLEN digs for a position
int len;
public:
BigNum(){len = 1;memset(a,0,sizeof(a));}
BigNum(const int b);
BigNum(const BigNum & T);
bool Bigger(const BigNum &) const;
BigNum & operator=(const BigNum &);
BigNum & Add(const BigNum &);
BigNum & Sub(const BigNum &);
BigNum operator+(const BigNum &) const;
BigNum operator-(const BigNum &) const;
BigNum operator*(const BigNum &) const;
BigNum operator/(const int &) const;
void Print();
};
BigNum::BigNum(const int b)
{
int c,d = b; len = 0;
memset(a,0,sizeof(a));
while(d > MAXN){
c = d - d / (MAXN + 1) * (MAXN + 1);
d = d / (MAXN + 1);
a[len++] = c;
}a[len++] = d;
}
BigNum::BigNum(const BigNum & T) : len(T.len)
{
int i; memset(a,0,sizeof(a));
for(i = 0 ; i < len ; i++) a[i] = T.a[i];
}
bool BigNum::Bigger(const BigNum & T) const
{
int ln;
if(len > T.len) return true;
else if(len == T.len){
ln = len - 1;
while(a[ln] == T.a[ln] && ln >= 0) ln--;
if(ln >= 0 && a[ln] > T.a[ln]) return true;
else return false;
}
else return false;
}
BigNum & BigNum::operator=(const BigNum & n)
{
len = n.len; memset(a,0,sizeof(a));
for(int i = 0 ; i < len ; i++)
a[i] = n.a[i];
return *this;
}
BigNum & BigNum::Add(const BigNum & T)
{
int i,big;
big = T.len > len ? T.len : len;
for(i = 0 ; i < big ; i++){
a[i] = a[i] + T.a[i];
if(a[i] > MAXN){
a[i + 1]++;
a[i] = a[i] - MAXN - 1;
}
}
if(a[big] != 0) len = big + 1;
else len = big;
return *this;
}
BigNum & BigNum::Sub(const BigNum & T)
{
int i,j,big;
big = T.len > len ? T.len : len;
for(i = 0 ; i < big ; i++){
if(a[i] < T.a[i]){
j = i + 1;
while(a[j] == 0) j++;
a[j--]--;
while(j > i) a[j--] += MAXN;
a[i] = a[i] + MAXN + 1 - T.a[i];
}
else a[i] -= T.a[i];
}
len = big;
while(a[len - 1] == 0 && len > 1) len--;
return *this;
}
BigNum BigNum::operator+(const BigNum & n) const
{
BigNum a = *this; a.Add(n);
return a;
}
BigNum BigNum::operator-(const BigNum & T) const
{
BigNum b = *this;b.Sub(T);
return b;
}
BigNum BigNum::operator*(const BigNum & T) const
{
BigNum ret;
int i,j,up;
int temp,temp1;
for(i = 0 ; i < len ; i++){
up = 0;
for(j = 0 ; j < T.len ; j++){
temp = a[i] * T.a[j] + ret.a[i + j] + up;
if(temp > MAXN){
temp1 = temp - temp / (MAXN + 1) * (MAXN + 1);
up = temp / (MAXN + 1);
ret.a[i + j] = temp1;
}
else {
up = 0;
ret.a[i + j] = temp;
}
}
if(up != 0)
ret.a[i + j] = up;
}
ret.len = i + j;
while(ret.a[ret.len - 1] == 0 && ret.len > 1) ret.len--;
return ret;
}
BigNum BigNum::operator/(const int & b) const
{
BigNum ret;
int i,down = 0;
for(i = len - 1 ; i >= 0 ; i--){
ret.a[i] = (a[i] + down * (MAXN + 1)) / b;
down = a[i] + down * (MAXN + 1) - ret.a[i] * b;
}
ret.len = len;
while(ret.a[ret.len - 1] == 0) ret.len--;
return ret;
}
void BigNum::Print()
{
int i;
cout << a[len - 1];
for(i = len - 2 ; i >= 0 ; i--){
cout.width(DLEN);
cout.fill('0');
cout << a[i];
}cout << endl;
//输出的时候注意这里的换行,注意C++的输出不能和C和输出混用
}
int main(){
int i;
BigNum s[101]; s[1]=1;
for(i=1;i<100;i++){
s[i+1]=BigNum(4*i+2)*(s[i])/(i+2);
}
while(scanf("%d",&i) && i != -1){
s[i].Print();
}
return 0;
}
37. 通项公式
//F[n]=a*F[n-1]+b*F[n-2]的通项公式的求解
//此类方程的特征方程为 x^2 - a^x - b*1 = 0;
//假设方程的解为q1,q2 ; F[n]=c1 * q1^n + c2 * q2^n
//将f[0] ,f[1]等已知的结果代入,就可求得c1,c2