目录
数据结构
1 字典树
字符串
1 Manacher最长回文子串
2 KMP
3 扩展KMP
4 AC自动机
图论
1 网络流dinic
2 zkw费用流
3 有向图的强联通分量
4 无向图的强联通分量
5 匈牙利匹配
6 最小生成树
7 最短路
8 欧拉回路Fleury
数论
1 中国剩余定理
2 大质数判定
3 约瑟夫环
博弈
1 Bash博弈
2 威佐夫博弈
3 Nim博弈
4 斐波那契博弈
5 Anti-num博弈
6 SG函数
其他
1 祖传头文件
2 大数
3 STL
4 正常的读入挂
5 fread读入挂
6 莫队算法
数据结构
1.字典树
使用时记得先初始化第一个点
const int MAXN = 1e2 + 5; int Maxlen, Maxnum; int T, K; struct node { int next[27]; int v, num; void init() { v=-1; num = 0; memset(next,-1,sizeof(next)); } }; struct node L[1000000]; int tot=0; void add(char a[]) { int now=0; int len = strlen(a); for(int i=0; i<len; i++) { int tmp=a[i]-'a'; int next=L[now].next[tmp]; if(next==-1) { next=++tot; L[next].init(); L[now].next[tmp]=next; } now=next; L[now].num ++; if(L[now].num >= 3) { if(Maxlen < i + 1) { Maxlen = i + 1; Maxnum = L[now].num; } else if(Maxnum < L[now].num && Maxlen <= i + 1) { Maxnum = L[now].num; } } } L[now].v=0; } int query(char s2[]) { int len = strlen(s2); int now = 0; for(int i = 0; i < len; i++) { int temp = s2[i] - 'a'; int next = L[now].next[temp]; if(next == -1) return 0; now = next; } return L[now].num; } int main() { L[0].init(); return 0; }
字符串
1 Manacher最长回文子串
const int MAX = 200000 + 10; char s[MAX * 2];//记得要开两倍 int p[MAX * 2]; int manacher(char *s) { int len = strlen(s), id = 0, ans = 0; for(int i = len; i >= 0; i--) { s[i + i + 2] = s[i]; s[i + i + 1] = '#'; } s[0] = '*';//防越界,很重要!! for(int i = 2; i < 2 * len + 1; ++i) { if(p[id] + id > i) p[i] = min(p[2 * id - i], p[id] + id - i); else p[i] = 1; while(s[i - p[i]] == s[i + p[i]]) p[i]++; if(id + p[id] < i + p[i]) id = i; ans = max(ans, p[i] - 1); } return ans; }
2.KMP
const int MX = 2000 + 5; /* int Next[MX], n; void GetNext() { Next[0] = 0; for(int i = 1; i < n; i++) { int j = Next[i - 1]; while(j && S[i] != S[j]) j = Next[j - 1]; Next[i] = S[i] == S[j] ? j + 1 : 0; } } /*求前缀i 循环节最长长度 int GetCir(int p) { return (p + 1) % (p - Next[p] + 1) == 0 ? p - Next[p] + 1 : p + 1; } */ /*会有重叠部分*/ int Next[MX]; int KMP(char *A, char *B) { int m = strlen(A), n = strlen(B); Next[0] = 0; for(int i = 1; i < n; i++) { int k = Next[i - 1]; while(B[i] != B[k] && k) k = Next[k - 1]; Next[i] = B[i] == B[k] ? k + 1 : 0; } int ans = 0, j = 0; for(int i = 0; i < m; i++) { while(A[i] != B[j] && j) j = Next[j - 1]; if(A[i] == B[j]) j++; if(j == n) ans++; } return ans; }
3.扩展KMP
/* * 扩展KMP算法 */ //next[i]:x[i...m-1]与x[0...m-1]的最长公共前缀 //extend[i]:y[i...n-1]与x[0...m-1]的最长公共前缀 void pre_EKMP(char x[], int m, int next[]) { next[0] = m; int j = 0; while(j + 1 < m && x[j] == x[j + 1])j++; next[1] = j; int k = 1; for(int i = 2; i < m; i++) { int p = next[k] + k - 1; int L = next[i - k]; if(i + L < p + 1)next[i] = L; else { j = max(0, p - i + 1); while(i + j < m && x[i + j] == x[j])j++; next[i] = j; k = i; } } } void EKMP(char x[], int m, char y[], int n, int next[], int extend[]) { pre_EKMP(x, m, next); int j = 0; while(j < n && j < m && x[j] == y[j])j++; extend[0] = j; int k = 0; for(int i = 1; i < n; i++) { int p = extend[k] + k - 1; int L = next[i - k]; if(i + L < p + 1)extend[i] = L; else { j = max(0, p - i + 1); while(i + j < n && j < m && y[i + j] == x[j])j++; extend[i] = j; k = i; } } }
4.AC自动机
struct Trie { int next[500010][26], fail[500010], end[500010]; int root, L; int newnode() { for(int i = 0; i < 26; i++) next[L][i] = -1; end[L++] = 0; return L - 1; } void init() { L = 0; root = newnode(); } void insert(char buf[]) { int len = strlen(buf); int now = root; for(int i = 0; i < len; i++) { if(next[now][buf[i] - 'a'] == -1) next[now][buf[i] - 'a'] = newnode(); now = next[now][buf[i] - 'a']; } end[now]++; } void build() { queue<int>Q; fail[root] = root; for(int i = 0; i < 26; i++) if(next[root][i] == -1) next[root][i] = root; else { fail[next[root][i]] = root; Q.push(next[root][i]); } while( !Q.empty() ) { int now = Q.front(); Q.pop(); for(int i = 0; i < 26; i++) if(next[now][i] == -1) next[now][i] = next[fail[now]][i]; else { fail[next[now][i]] = next[fail[now]][i]; Q.push(next[now][i]); } } } int query(char buf[]) { int len = strlen(buf); int now = root; int res = 0; for(int i = 0; i < len; i++) { now = next[now][buf[i] - 'a']; int temp = now; while( temp != root ) { res += end[temp]; end[temp] = 0; temp = fail[temp]; } } return res; } void debug() { for(int i = 0; i < L; i++) { printf("id = %3d,fail = %3d,end = %3d,chi = [", i, fail[i], end[i]); for(int j = 0; j < 26; j++) printf("%2d", next[i][j]); printf("] "); } } }; Trie AC;
图论
1.网络流Dinic
const int maxn = 405; //开四倍 const int maxe = 4*maxn*maxn; const int inf = 0x3f3f3f3f; struct MaxFlow { struct Edge { int v, w, nxt; } edge[maxe]; int head[maxn], tot, level[maxn]; void init(){ memset(head,-1,sizeof(head)); tot=0; } void add(int u, int v, int w) { edge[tot].v = v; edge[tot].w = w; edge[tot].nxt = head[u]; head[u] = tot++; edge[tot].v = u; edge[tot].w = 0; edge[tot].nxt = head[v]; head[v] = tot++; } bool bfs(int s, int t) { memset(level, -1, sizeof(level)); queue<int>q; q.push(s); level[s] = 0; while(!q.empty()) { int u = q.front(); q.pop(); for(int i = head[u]; ~i; i = edge[i].nxt) { if(edge[i].w > 0 && level[edge[i].v] < 0) { level[edge[i].v] = level[u] + 1; q.push(edge[i].v); } } } return level[t] > 0; } int dfs(int u, int t, int f) { if(u == t) return f; for(int i = head[u]; ~i; i = edge[i].nxt) { int v = edge[i].v; if(edge[i].w > 0 && level[v] > level[u]) { int d = dfs(v, t, min(f, edge[i].w)); if(d > 0) { edge[i].w -= d; edge[i ^ 1].w += d; return d; } } } level[u] = -1; //不太确定,如果WA了把这句删掉试试 return 0; } int solve(int s, int t) { int flow = 0, f; while(bfs(s, t)) { while(f = dfs(s, t, inf)) flow += f; } return flow; } }F;
2.
zkw费用流
const int MX = 80000; const int MXE = 4 * MX * MX; const int ME = 4e5 + 5;//边的数量 struct MCMF { int S, T;//源点,汇点 int tot, n; int st, en, maxflow, mincost; bool vis[MX]; int head[MX], cur[MX], dis[MX]; int roade[MX], roadv[MX], rsz; //用于打印路径 queue <int> Q; struct Edge { int v, cap, cost, nxt, flow; Edge() {} Edge(int a, int b, int c, int d) { v = a, cap = b, cost = c, nxt = d, flow = 0; } } E[ME], SE[ME]; void init(int _n) { n = _n, tot = 0; for(int i = 0; i <= n; i++) head[i] = -1; } void edge_add(int u, int v, int cap, int cost) { E[tot] = Edge(v, cap, cost, head[u]); head[u] = tot++; E[tot] = Edge(u, 0, -cost, head[v]); head[v] = tot++; } bool adjust() { int v, min = INF; for(int i = 0; i <= n; i++) { if(!vis[i]) continue; for(int j = head[i]; ~j; j = E[j].nxt) { v = E[j].v; if(E[j].cap - E[j].flow) { if(!vis[v] && dis[v] - dis[i] + E[j].cost < min) { min = dis[v] - dis[i] + E[j].cost; } } } } if(min == INF) return false; for(int i = 0; i <= n; i++) { if(vis[i]) { cur[i] = head[i]; vis[i] = false; dis[i] += min; } } return true; } int augment(int i, int flow) { if(i == en) { mincost += dis[st] * flow; maxflow += flow; return flow; } vis[i] = true; for(int j = cur[i]; j != -1; j = E[j].nxt) { int v = E[j].v; if(E[j].cap == E[j].flow) continue; if(vis[v] || dis[v] + E[j].cost != dis[i]) continue; int delta = augment(v, std::min(flow, E[j].cap - E[j].flow)); if(delta) { E[j].flow += delta; E[j ^ 1].flow -= delta; cur[i] = j; return delta; } } return 0; } void spfa() { int u, v; for(int i = 0; i <= n; i++) { vis[i] = false; dis[i] = INF; } Q.push(st); dis[st] = 0; vis[st] = true; while(!Q.empty()) { u = Q.front(), Q.pop(); vis[u] = false; for(int i = head[u]; ~i; i = E[i].nxt) { v = E[i].v; if(E[i].cap == E[i].flow || dis[v] <= dis[u] + E[i].cost) continue; dis[v] = dis[u] + E[i].cost; if(!vis[v]) { vis[v] = true; Q.push(v); } } } for(int i = 0; i <= n; i++) { dis[i] = dis[en] - dis[i]; } } int zkw(int s, int t) { st = s, en = t; spfa(); mincost = maxflow = 0; for(int i = 0; i <= n; i++) { vis[i] = false; cur[i] = head[i]; } do { while(augment(st, INF)) { memset(vis, false, n * sizeof(bool)); } } while(adjust()); return mincost; } } M;
3.有向图的强联通分量缩点
const int MX = 5e3 + 5; const int INF = 0x3f3f3f3f; struct Edge { int u, v, nxt; } E[60005]; int Head[MX], erear; void edge_init() { erear = 0; memset(Head, -1, sizeof(Head)); } void edge_add(int u, int v) { E[erear].u = u; E[erear].v = v; E[erear].nxt = Head[u]; Head[u] = erear++; } int n, m, IN[MX], cnt[MX], val[MX]; int bsz, ssz, dsz; int Low[MX], DFN[MX]; int belong[MX], Stack[MX]; bool inStack[MX]; void Init_tarjan(int n) { bsz = ssz = dsz = 0; for(int i = 1; i <= n; ++i) Low[i] = DFN[i] = 0; } void Tarjan(int u) { Stack[++ssz] = u; inStack[u] = 1; Low[u] = DFN[u] = ++dsz; for(int i = Head[u]; ~i; i = E[i].nxt) { int v = E[i].v; if(!DFN[v]) { Tarjan(v); Low[u] = min( Low[v], Low[u]); } else if(inStack[v]) { Low[u] = min( Low[u], DFN[v]); } } if(Low[u] == DFN[u]) { ++bsz; int v; do { v = Stack[ssz--]; inStack[v] = 0; belong[v] = bsz; } while(u != v); } } int solve(int n) { Init_tarjan(n); for (int i = 1; i <= n; i++) { if (!DFN[i]) Tarjan(i); } edge_init(); for(int i = 0; i < m; i++) { int u = E[i].u, v = E[i].v; u = belong[u]; v = belong[v]; if(u != v) { edge_add(u, v); } } }
4.无向图的强联通分量缩点
const int MX = 1e5 + 10; struct Edge { int u, v, nxt; } E[MX]; int Head[MX], erear; void edge_init() { erear = 0; memset(Head, -1, sizeof(Head)); } void edge_add(int u, int v) { E[erear].u = u; E[erear].v = v; E[erear].nxt = Head[u]; Head[u] = erear++; } int n, m, IN[MX], cnt[MX], val[MX]; int bsz, ssz, dsz; int Low[MX], DFN[MX]; void Init_tarjan(int n) { bsz = ssz = dsz = 0; for(int i = 1; i <= n; ++i) Low[i] = DFN[i] = 0; } int Stack[MX], inStack[MX], Belong[MX]; void trajan(int u, int e) { inStack[u] = 1; Stack[++ssz] = u; DFN[u] = Low[u] = ++dsz; for(int i = Head[u]; ~i; i = E[i].nxt) { int v = E[i].v; if((i ^ 1) == e) continue; if(!DFN[v]) { trajan(v, i); Low[u] = min(Low[u], Low[v]); } else if(inStack[v]) { Low[u] = min(Low[u], Low[v]); } } if(DFN[u] == Low[u]) { bsz++; int v; do { v = Stack[ssz--]; inStack[v] = 0; Belong[v] = bsz; } while(ssz && v != u); } } void tarjan_solve(int n) { dsz = bsz = ssz = 0; memset(DFN, 0, sizeof(DFN)); for(int i = 1; i <= n; i++) { if(!DFN[i]) trajan(i, -1); } /*缩点*/ edge_init(); for(int i = 0; i < 2 * m; i += 2) { int u = E[i].u, v = E[i].v; u = Belong[u]; v = Belong[v]; if(u == v) continue; edge_add(u, v); edge_add(v, u); } }
5.匈牙利匹配
/*复杂度O(VE) 最小点覆盖=最大匹配数 最小边覆盖=左右点数-最大匹配数 最小路径覆盖=点数-最大匹配数 最大独立集=点数-最大匹配数 */ const int MX = 205; struct Edge { int v, nxt; }E[MX]; int Head[MX], tot; int match[MX]; bool vis[MX]; void edge_init() { memset(Head, -1, sizeof(Head)); memset(match, 0, sizeof(match)); tot = 0; } void edge_add(int u, int v) { E[tot].v = v; E[tot].nxt = Head[u]; Head[u] = tot++; } bool DFS(int u) { for(int i = Head[u]; ~i; i = E[i].nxt) { int v = E[i].v; if(!vis[v]) { vis[v] = 1; if(match[v] == -1 || DFS(match[v])) { //如果v已经被匹配了,且被匹配的点不是 //当前点u,那么就去修改这个match[v],如果修改成功了,那么就可以返回1 match[v] = u; return 1; } } } return 0; } int BM(int n) { int res = 0; memset(match, -1, sizeof(match)); for(int u = 1; u <= n; u++) { memset(vis, 0, sizeof(vis)); if(DFS(u)) res++;//找与u匹配的点v,如果找到了答案就+1 } return res; }
6.最小生成树
/* * Kruskal算法求MST */ const int MAXN = 1000 + 5; //最大点数 const int MAXM = 50000 + 5; //最大边数 int F[MAXN];//并查集使用 struct Edge { int u, v, w; } edge[MAXM]; //存储边的信息,包括起点/终点/权值 int tol;//边数,加边前赋值为0 void addedge(int u, int v, int w) { edge[tol].u = u; edge[tol].v = v; edge[tol++].w = w; } bool cmp(Edge a, Edge b) { //排序函数,讲边按照权值从小到大排序 return a.w < b.w; } int find(int x) { if(F[x] == -1)return x; else return F[x] = find(F[x]); } int Kruskal(int n) { //传入点数,返回最小生成树的权值,如果不连通返回-1 memset(F, -1, sizeof(F)); sort(edge, edge + tol, cmp); int cnt = 0; //计算加入的边数 int ans = 0; for(int i = 0; i < tol; i++) { int u = edge[i].u; int v = edge[i].v; int w = edge[i].w; int t1 = find(u); int t2 = find(v); if(t1 != t2) { ans += w; F[t1] = t2; cnt++; } if(cnt == n - 1)break; } if(cnt < n - 1)return -1; //不连通 else return ans; } /* * Prim求MST * 耗费矩阵cost[][],标号从0开始,0~n-1 * 返回最小生成树的权值,返回-1表示原图不连通 */ const int INF = 0x3f3f3f3f; const int MAXN = 110; bool vis[MAXN]; int lowc[MAXN]; int Prim(int cost[][MAXN], int n) { //点是0~n-1 int ans = 0; memset(vis, false, sizeof(vis)); vis[0] = true; for(int i = 1; i < n; i++)lowc[i] = cost[0][i]; for(int i = 1; i < n; i++) { int minc = INF; int p = -1; for(int j = 0; j < n; j++) if(!vis[j] && minc > lowc[j]) { minc = lowc[j]; p = j; } if(minc == INF)return -1; //原图不连通 ans += minc; vis[p] = true; for(int j = 0; j < n; j++) if(!vis[j] && lowc[j] > cost[p][j]) lowc[j] = cost[p][j]; } return ans; }
7.最短路
1、最短路 1.1 Dijkstra单源最短路,邻接矩阵形式 权值必须是非负 /* * 单源最短路径,Dijkstra算法,邻接矩阵形式,复杂度为O(n^2) * 求出源beg到所有点的最短路径,传入图的顶点数,和邻接矩阵cost[][] * 返回各点的最短路径lowcost[], 路径pre[].pre[i]记录beg到i路径上的父结点,pre[beg]=-1 * 可更改路径权类型,但是权值必须为非负 * */ const int MAXN = 1010; #define typec int const typec INF = 0x3f3f3f3f; //防止后面溢出,这个不能太大 bool vis[MAXN]; int pre[MAXN]; void Dijkstra(typec cost[][MAXN], typec lowcost[], int n, int beg) { for(int i = 0; i < n; i++) { lowcost[i] = INF; vis[i] = false; pre[i] = -1; } lowcost[beg] = 0; for(int j = 0; j < n; j++) { int k = -1; int Min = INF; for(int i = 0; i < n; i++) if(!vis[i] && lowcost[i] < Min) { Min = lowcost[i]; k = i; } if(k == -1)break; vis[k] = true; for(int i = 0; i < n; i++) if(!vis[i] && lowcost[k] + cost[k][i] < lowcost[i]) { lowcost[i] = lowcost[k] + cost[k][i]; pre[i] = k; } } } 1.2 Dijkstar 算法 +堆优化 /* * 使用优先队列优化Dijkstra算法 * 复杂度O(ElogE) * 注意对vector<Edge>E[MAXN]进行初始化后加边 */ const int INF = 0x3f3f3f3f; const int MAXN = 1000010; struct qnode { int v; int c; qnode(int _v = 0, int _c = 0): v(_v), c(_c) {} bool operator <(const qnode &r)const { return c > r.c; } }; struct Edge { int v, cost; Edge(int _v = 0, int _cost = 0): v(_v), cost(_cost) {} }; vector<Edge>E[MAXN]; bool vis[MAXN]; int dist[MAXN]; void Dijkstra(int n, int start) { //点的编号从1开始 memset(vis, false, sizeof(vis)); for(int i = 1; i <= n; i++)dist[i] = INF; priority_queue<qnode>que; while(!que.empty())que.pop(); dist[start] = 0; que.push(qnode(start, 0)); qnode tmp; while(!que.empty()) { tmp = que.top(); que.pop(); int u = tmp.v; if(vis[u])continue; vis[u] = true; for(int i = 0; i < E[u].size(); i++) { int v = E[tmp.v][i].v; int cost = E[u][i].cost; if(!vis[v] && dist[v] > dist[u] + cost) { dist[v] = dist[u] + cost; que.push(qnode(v, dist[v])); } } } } void addedge(int u, int v, int w) { E[u].push_back(Edge(v, w)); } 1.3 单源最短路 单源最短路 bellman_ford /* * 单源最短路bellman_ford算法,复杂度O(VE) * 可以处理负边权图。 * 可以判断是否存在负环回路。返回true,当且仅当图中不包含从源点可达的负权回路 * vector<Edge>E;先E.clear()初始化,然后加入所有边 * 点的编号从1开始(从0开始简单修改就可以了) */ const int INF = 0x3f3f3f3f; const int MAXN = 550; int dist[MAXN]; struct Edge { int u, v; int cost; Edge(int _u = 0, int _v = 0, int _cost = 0): u(_u), v(_v), cost(_cost) {} }; vector<Edge>E; bool bellman_ford(int start, int n) { //点的编号从1开始 for(int i = 1; i <= n; i++)dist[i] = INF; dist[start] = 0; for(int i = 1; i < n; i++) { //最多做n-1次 bool flag = false; for(int j = 0; j < E.size(); j++) { int u = E[j].u; int v = E[j].v; int cost = E[j].cost; if(dist[v] > dist[u] + cost) { dist[v] = dist[u] + cost; flag = true; } } if(!flag)return true;//没有负环回路 } for(int j = 0; j < E.size(); j++) if(dist[E[j].v] > dist[E[j].u] + E[j].cost) return false;//有负环回路 return true;//没有负环回路 } 1.4 单源最短路 单源最短路 单源最短路 SPFA /* * 单源最短路SPFA * 时间复杂度 0(kE) * 这个是队列实现,有时候改成栈实现会更加快,很容易修改 * 这个复杂度是不定的 */ const int MAXN = 1010; const int INF = 0x3f3f3f3f; struct Edge { int v; int cost; Edge(int _v = 0, int _cost = 0): v(_v), cost(_cost) {} }; vector<Edge>E[MAXN]; void addedge(int u, int v, int w) { E[u].push_back(Edge(v, w)); } bool vis[MAXN];//在队列标志 int cnt[MAXN];//每个点的入队列次数 int dist[MAXN]; bool SPFA(int start, int n) { memset(vis, false, sizeof(vis)); for(int i = 1; i <= n; i++)dist[i] = INF; vis[start] = true; dist[start] = 0; queue<int>que; while(!que.empty())que.pop(); que.push(start); memset(cnt, 0, sizeof(cnt)); cnt[start] = 1; while(!que.empty()) { int u = que.front(); que.pop(); vis[u] = false; for(int i = 0; i < E[u].size(); i++) { int v = E[u][i].v; if(dist[v] > dist[u] + E[u][i].cost) { dist[v] = dist[u] + E[u][i].cost; if(!vis[v]) { vis[v] = true; que.push(v); if(++cnt[v] > n)return false; //cnt[i]为入队列次数,用来判定是否存在负环回路 } } } } return true; }
8.欧拉回路Fleury
/*删边要注意复杂度,尽量别用标记删除,而是直接删除 无向图满足欧拉回路:度为偶数,或者度为奇数的点个数为2 有向图满足欧拉回路:入度全部等于出度,或者1 个点入度-出度=1,一个点出度-入度=1,其他点入度等于出度 */ void Fleury(int u) { for(int i = Head[u]; ~i; i = Head[u]) { Head[u] = E[i].nxt; if(!vis[i | 1]) { int v = E[i].v; vis[i | 1] = 1; Fleury(v); } } Path[++r] = u; }
数论
1.中国剩余定理
LL gcd(LL a, LL b, LL &x, LL &y) { if(b == 0) { x =1, y = 0; return a; } LL r = gcd(b, a % b, x, y); LL t = y; y = x - a / b * y; x = t; return r; } LL multi(LL a, LL b, LL mod) { LL ret = 0; while(b) { if(b & 1) { ret = ret + a; if(ret >= mod) ret -= mod; } a = a + a; if(a >= mod) a -= mod; b >>= 1; } return ret; } LL crt(int n, LL m[], LL a[]) { LL M = 1, d, y, x = 0; for(int i = 0; i < n; i++) M *= m[i]; for(int i = 0; i < n; i++) { LL w = M / m[i]; d = gcd(m[i], w, d, y); y = (y % m[i] + m[i]) % m[i]; x = ((x + multi(multi(a[i], w, M), y, M)) % M + M) % M; } return x; }
2.大质数判定
LL multi(LL a, LL b, LL mod) { LL ret = 0; while(b) { if(b & 1) ret = ret + a; if(ret >= mod) ret -= mod; a = a + a; if(a >= mod) a -= mod; b >>= 1; } return ret; } LL power(LL a, LL b, LL mod) { LL ret = 1; while(b) { if(b & 1) ret = multi(ret, a, mod); a = multi(a, a, mod); b >>= 1; } return ret; } bool Miller_Rabin(LL n) { LL u = n - 1, pre, x; int i, j, k = 0; if(n == 2 || n == 3 || n == 5 || n == 7 || n == 11) return true; if(n == 1 || (!(n % 2)) || (!(n % 3)) || (!(n % 5)) || (!(n % 7)) || (!(n % 11))) return false; for(; !(u & 1); k++, u >>= 1); for(i = 0; i < 5; i++) { x = rand() % (n - 2) + 2; x = power(x, u, n); pre = x; for(j = 0; j < k; j++) { x = multi(x, x, n); if(x == 1 && pre != 1 && pre != (n - 1)) return false; pre = x; } if(x != 1) return false; } return true; }
3.约瑟夫环
/* F[n] = (F[n - 1] + m) % n, F[1] = 0 返回的下标从0 开始,复杂度大约为O(m) */ int Joseph(int n, int m) { if(n == 1) return 0; if(m == 1) return n - 1; LL pre = 0; int now = 2; while(now <= n) { if(pre + m >= now) { pre = (pre + m) % now; now++; } else { int a = now - 1 - pre, b = m - 1; int k = a / b + (a % b != 0); if(now + k > n + 1) k = n + 1 - now; pre = (pre + (LL)m * k) % (now + k - 1); now += k; } } return pre; }
博弈
1.Bash博弈
题意:一堆石头共n个,两人轮流取,最少取一个,最多取m个。最后取完的人赢
思路:当m % (n + 1) != 0时先手赢,否则后手赢
2.威佐夫博弈
题意:两堆石子数量分别为a,b。两人轮流取石子,取石子时可以1.从一堆中取任意个
数 2.在两堆中取相同个数。最后取完的人获胜。
思路:满足黄金分割
if(a >= b) swap(a, b);
int k = b - a;
int x = (sqrt(5.0) + 1) / 2 * k, y = x + k;
if(a == x && b == y) printf("B
");
else printf("A
");
3.Nim博弈
题意:有n堆石子。A B两个人轮流拿,A先拿。每次只能从一堆中取若干个,可将一堆
全取走,但不可不取,拿到最后1颗石子的人获胜。
思路:Nim博弈相当于把独立游戏分开计算SG函数,然后再用位异或
SG[u] = Mex({后继的集合})相当于取出最小的集合中不存在的数字,可以发现mex的值
总是比后继的个数要少,而且vis数组通常都是开在函数内部,不开在全局变量中,防
止冲突
4.斐波那契博弈
题意:1堆石子n个,第一个人可以取任意个数但不能全部取完,以后每次拿的个数不能
超过上一次对手拿的个数的2倍,轮流拿石子。
思路:后手赢得情况的数字会呈现斐波那契数列
5.Anti-num 博弈
SG函数的求法一模一样最后如果只有一堆也能用SJ定理
如果为Anti-Nim 游戏,如下情况先手胜
SG异或和为0,且单个游戏的SG全部<=1
SG 异或不为0,且存在单个游戏的 SG>1,即<=1的个数不等于独立游戏个数
6.SG函数
/** * 求出SG函数 * F(存储可以走的步数,Array[0]表示可以有多少种走法) * F[]需要从小到大排序 * 1.可选步数为1-m的连续整数,直接取模即可,SG(x)=x%(m+1); * 2.可选步数为任意步,SG(x) = x; * 3.可选步数为一系列不连续的数,用GetSG(计算) **/ int SG[MAX], Hash[MAX]; void init(int F[], int n) { int i, j; memset(SG, 0, sizeof(SG)); for(i = 0; i <= n; i++) { memset(Hash, 0, sizeof(Hash)); for(j = 1; j <= F[0]; j++) { if(i < F[j]) break; Hash[SG[i - F[j]]] = 1; } for(j = 0; j <= n; j++) { if(Hash[j] == 0) { SG[i] = j; break; } } } } /** * 获得一个单独的SG值 * k为可走步数,F数组存储可走步数(0~k-1) */ int F[101], sg[10001], k; int getsg(int m) { int hash[101] = {0}; int i; for(i = 0; i < k; i++) { if(m - F[i] < 0) break; if(sg[m - F[i]] == -1) sg[m - F[i]] = getsg(m - F[i]); hash[sg[m - F[i]]] = 1; } for(i = 0;; i++) if(hash[i] == 0) return i; }
其他
1.祖传头文件
#include <iostream> #include <cstring> #include <stdio.h> #include <algorithm> #include <queue> #include <vector> #include <iomanip> #include <math.h> #include <map> using namespace std; #define FIN freopen("input.txt","r",stdin); #define FOUT freopen("output.txt","w",stdout); #define INF 0x3f3f3f3f #define INFLL 0x3f3f3f3f3f3f3f #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 typedef long long LL; typedef pair<int, int> PII;
2.大数
const int MX = 2500; const int MAXN = 9999; const int DLEN = 4; /*已重载>+- % 和print*/ class Big { public: int a[MX], len; Big(const int b = 0) { 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; } Big(const char *s) { int t, k, index, L, i; memset(a, 0, sizeof(a)); L = strlen(s); len = L / DLEN; if(L % DLEN) len++; index = 0; for(i = L - 1; i >= 0; i -= DLEN) { t = 0; k = i - DLEN + 1; if(k < 0) k = 0; for(int j = k; j <= i; j++) { t = t * 10 + s[j] - '0'; } a[index++] = t; } } Big operator/(const int &b)const { Big ret; int i, down = 0; for(int 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 > 1) ret.len--; return ret; } bool operator>(const Big &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; } Big operator+(const Big &T)const { Big t(*this); int i, big; big = T.len > len ? T.len : len; for(i = 0; i < big; i++) { t.a[i] += T.a[i]; if(t.a[i] > MAXN) { t.a[i + 1]++; t.a[i] -= MAXN + 1; } } if(t.a[big] != 0) t.len = big + 1; else t.len = big; return t; } Big operator-(const Big &T)const { int i, j, big; bool flag; Big t1, t2; if(*this > T) { t1 = *this; t2 = T; flag = 0; } else { t1 = T; t2 = *this; flag = 1; } big = t1.len; for(i = 0; i < big; i++) { if(t1.a[i] < t2.a[i]) { j = i + 1; while(t1.a[j] == 0) j++; t1.a[j--]--; while(j > i) t1.a[j--] += MAXN; t1.a[i] += MAXN + 1 - t2.a[i]; } else t1.a[i] -= t2.a[i]; } t1.len = big; while(t1.a[t1.len - 1] == 0 && t1.len > 1) { t1.len--; big--; } if(flag) t1.a[big - 1] = 0 - t1.a[big - 1]; return t1; } int operator%(const int &b)const { int i, d = 0; for(int i = len - 1; i >= 0; i--) { d = ((d * (MAXN + 1)) % b + a[i]) % b; } return d; } Big operator*(const Big &T) const { Big ret; int i, j, up, 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; } void print() { printf("%d", a[len - 1]); for(int i = len - 2; i >= 0; i--) printf("%04d", a[i]); } };
3.STL
1.vector 构造 vector <int> v; 基本操作 v.begin() //指向迭代器中第一个元素。 v.end() //指向迭代器中末端元素的下一个,指向一个不存在元素。 v.push_back(elem) //在尾部加入一个数据。 v.pop_back() //删除最后一个数据。 v.capacity() //vector可用空间的大小。 v.size() //返回容器中数据个数。 v.empty() //判断容器是否为空。 v.front() //传回第一个数据。 v.back() //传回最后一个数据,不检查这个数据是否存在。 v.at(index) //传回索引idx所指的数据,如果idx越界,抛出out_of_range。 v.clear() //移除容器中所有数据。 v.erase(iterator) //删除pos位置的数据,传回下一个数据的位置。 v.erase(begin,end) //删除[beg,end)区间的数据,传回下一个数据的位置。注意:begin和end为iterator v.insert(position,elem) //在pos位置插入一个elem拷贝,传回新数据位置。 v.insert(position,n,elem) //在pos位置插入n个elem数据,无返回值。 v.insert(position,begin,end) //在pos位置插入在[beg,end)区间的数据,无返回值。 2.priority_queue 构造 priority_queue<int>que; //采用默认优先级构造队列最大值先出队 priority_queue<int,vector<int>,greater<int> >que3; //最小值先出队 priority_queue<int,vector<int>,less<int> >que4; //最大值先出队 基本操作 que.empty() //判断一个队列是否为空 que.pop() //删除队顶元素 que.push() //加入一个元素 que.size() //返回优先队列中拥有的元素个数 que.top() //返回优先队列的队顶元素 3.map 构造 map<int, string> mp; 基本操作 mp.begin() //返回指向map头部的迭代器 mp.clear() //删除所有元素 mp.count() //返回指定元素出现的次数 mp.empty() //如果map为空则返回true mp.end() //返回指向map末尾的迭代器 mp.equal_range() //返回特殊条目的迭代器对 mp.erase() //删除一个元素 mp.find() //查找一个元素 mp.get_allocator() //返回map的配置器 mp.insert() //插入元素 mp.key_comp() //返回比较元素key的函数 mp.lower_bound() //返回键值>=给定元素的第一个位置 mp.max_size() //返回可以容纳的最大元素个数 mp.rbegin() //返回一个指向map尾部的逆向迭代器 mp.rend() //返回一个指向map头部的逆向迭代器 mp.size() //返回map中元素的个数 mp.swap() //交换两个map mp.upper_bound() //返回键值>给定元素的第一个位置 mp.value_comp() //返回比较元素value的函数 4.queue 构造 queue<int> q; 基本操作 q.push(num) //入列,插入队列的末端。 q.empty() //判断队列是否为空 q.size() //获取队列中的元素个数 q.back() //访问队列尾元素 5.stack 构造 stack<int> s; stack<int> c1(c2); 复制stack 基本操作 s.top() //返回栈顶数据 s.push(num) //在栈顶插入数据 s.pop() //弹出栈顶数据 s.empty() //判断栈是否为空 s.size() //返回栈中数据的个数 6.set 构造 set<int> s; 基本操作 s.insert(num) //把元素插入到集合中,同一元素不会重复插入 s.inset(first,second) //将定位器first到second之间的元素插入到set中,返回值是void s.erase(6) //删除键值为6的元素 s.find(6) //查找键值为6的元素,如果没找到则返回end() s.begin() //返回set容器的第一个元素 s.end() //返回set容器的最后一个元素 s.clear() //删除set容器中的所有的元素 s.empty() //判断set容器是否为空 s.max_size() //返回set容器可能包含的元素最大个数 s.size() //返回当前set容器中的元素个数 s.rbegin() //返回的值和end()相同 s.rend() //返回的值和rbegin()相同 s.count() //用来查找set中某个某个键值出现的次数,一般用于判断某一键值是否在set出现过 erase(iterator) //删除定位器iterator指向的值 erase(first,second) //删除定位器first和second之间的值(不包括second) erase(key_value) //删除键值key_value的值 lower_bound(key_value) //返回第一个大于等于key_value的定位器 upper_bound(key_value) //返回最后一个大于等于key_value的定位器 7.deque 构造 deque<int> c; deque<int> c1(c2); //复制deque deque<int> c(n, elem); //创建一个含有n个elem拷贝的deque 基本操作 c.assign(beg, end); //将beg-end区间中的值赋值给c c.assign(n, elem); //将n个elem的拷贝赋值给c c.at(idx); //返回idx位置所指数据 c.front(); //返回第一个数据 c.back(); //返回最后一个数据 c.begin(); //返回指向第一个数据的迭代器 c.end(); //返回指向最后一个数据的下一个位置的迭代器 c.rbegin(); //返回逆向队列的第一个数据 c.rend(); //返回指向逆向队列的最后一个数据的下一个位置的迭代器 c.push_back(elem); //在尾部加入一个数据 c.push_front(elem); //在头部插入一个数据 c.insert(pos, elem); //在pos位置插入一个elem拷贝,返回新数据位置 c.insert(pos, n, elem); //在pos位置插入n个elem数据,无返回值 c.insert(pos, beg, end); //在pos位置插入在beg-end区间的数据,无返回值 c.pop_back(); //删除最后一个数据 c.pop_front(); //删除头部数据 c.erase(pos); //删除pos位置的数据,返回下一个数据的位置 c.erase(beg, end); //删除beg-end区间的数据,返回下一个数据的位置 c.empty(); //判断容器是否为空 c.max_size(); //返回容器中最大数据的数量 c.resize(num); //重新指定队列的长度 c.size(); //返回容器中实际数据的个数 c1.swap(c2) //将c1和c2元素互换
4.正常的读入挂
inline int read() { int ret = 0, c, f = 1; for(c = getchar(); !(isdigit(c) || c == '-'); c = getchar()); if(c == '-') f = -1, c = getchar(); for(; isdigit(c); c = getchar()) ret = ret * 10 + c - '0'; if(f < 0) ret = -ret; return ret; }
5.fread读入挂
namespace IO { const int MX = 1e7; //1e7 占用内存11000kb char buf[MX]; int c, sz; void begin() { c = 0; sz = fread(buf, 1, MX, stdin); } inline bool read(int &t) { while(c < sz && buf[c] != '-' && (buf[c] < '0' || buf[c] > '9')) c++; if(c >= sz) return false; bool flag = 0; if(buf[c] == '-') flag = 1, c++; for(t = 0; c < sz && '0' <= buf[c] && buf[c] <= '9'; c++) t = t * 10 + buf[c] - '0'; if(flag) t = -t; return true; } }
6.莫队算法
莫队算法,可以解决一类静态,离线区间查询问题。 BZOJ 2038: [2009 国家集训队]小Z 的袜子(hose) Description 作为一个生活散漫的人,小Z 每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。 终于有一天,小Z 再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说, 小Z 把这N 只袜子从1 到N 编号,然后从编号L 到R(L Input 输入文件第一行包含两个正整数N 和M。N 为袜子的数量,M 为小Z 所提的询问的数量。 接下来一行包含N 个正整数Ci,其中Ci 表示第i 只袜子的颜色,相同的颜色用相同的数字 表示。再接下来M 行,每行两个正整数L,R 表示一个询问。 Output 包含M 行,对于每个询问在一行中输出分数A / B 表示从该询问的区间[L, R]中随机抽出两只 袜子颜色相同的概率。若该概率为0 则输出0 / 1,否则输出的A / B 必须为最简分数。(详见 样例) Sample Input 6 4 1 2 3 3 3 2 2 6 1 3 3 5 1 6 Sample Output 2 / 5 0 / 1 1 / 1 4 / 15 只需要统计区间内各个数出现次数的平方和 莫队算法,两种方法,一种是直接分成sqrt(n)块,分块排序。 另外一种是求得曼哈顿距离最小生成树,根据manhattan MST 的dfs 序求解。 1 分块 const int MAXN = 50010; const int MAXM = 50010; struct Query { int L, R, id; } node[MAXM]; long long gcd(long long a, long long b) { if(b == 0)return a; return gcd(b, a % b); } struct Ans { long long a, b; //分数a/b void reduce() { //分数化简 long long d = gcd(a, b); a /= d; b /= d; } } ans[MAXM]; int a[MAXN]; int num[MAXN]; int n, m, unit; bool cmp(Query a, Query b) { if(a.L / unit != b.L / unit)return a.L / unit < b.L / unit; else return a.R < b.R; } void work() { long long temp = 0; memset(num, 0, sizeof(num)); int L = 1; int R = 0; for(int i = 0; i < m; i++) { while(R < node[i].R) { R++; temp -= (long long)num[a[R]] * num[a[R]]; num[a[R]]++; temp += (long long)num[a[R]] * num[a[R]]; } while(R > node[i].R) { temp -= (long long)num[a[R]] * num[a[R]]; num[a[R]]--; temp += (long long)num[a[R]] * num[a[R]]; R--; } while(L < node[i].L) { temp -= (long long)num[a[L]] * num[a[L]]; num[a[L]]--; temp += (long long)num[a[L]] * num[a[L]]; L++; } while(L > node[i].L) { L--; temp -= (long long)num[a[L]] * num[a[L]]; num[a[L]]++; temp += (long long)num[a[L]] * num[a[L]]; } ans[node[i].id].a = temp - (R - L + 1); ans[node[i].id].b = (long long)(R - L + 1) * (R - L); ans[node[i].id].reduce(); } } int main() { //FIN while(~scanf("%d%d", &n, &m)) { for(int i = 1; i <= n; i++) scanf("%d", &a[i]); for(int i = 0; i < m; i++) { node[i].id = i; scanf("%d%d", &node[i].L, &node[i].R); } unit = sqrt(n); sort(node , node + m, cmp); work(); for(int i = 0; i < m; i++) printf("%lld/%lld ", ans[i].a, ans[i].b); } return 0; } 2 Manhattan MST的dfs顺序求解 const int MAXN = 50010; const int MAXM = 50010; const int INF = 0x3f3f3f3f; struct Point { int x, y, id; } p[MAXN], pp[MAXN]; bool cmp(Point a, Point b) { if(a.x != b.x) return a.x < b.x; else return a.y < b.y; } //树状数组,找y-x大于当前的,但是y+x最小的 struct BIT { int min_val, pos; void init() { min_val = INF; pos = -1; } } bit[MAXN]; struct Edge { int u, v, d; } edge[MAXN << 2]; bool cmpedge(Edge a, Edge b) { return a.d < b.d; } int tot; int n; int F[MAXN]; int find(int x) { if(F[x] == -1) return x; else return F[x] = find(F[x]); } void addedge(int u, int v, int d) { edge[tot].u = u; edge[tot].v = v; edge[tot++].d = d; } struct Graph { int to, next; } e[MAXN << 1]; int total, head[MAXN]; void _addedge(int u, int v) { e[total].to = v; e[total].next = head[u]; head[u] = total++; } int lowbit(int x) { return x & (-x); } void update(int i, int val, int pos) { while(i > 0) { if(val < bit[i].min_val) { bit[i].min_val = val; bit[i].pos = pos; } i -= lowbit(i); } } int ask(int i, int m) { int min_val = INF, pos = -1; while(i <= m) { if(bit[i].min_val < min_val) { min_val = bit[i].min_val; pos = bit[i].pos; } i += lowbit(i); } return pos; } int dist(Point a, Point b) { return abs(a.x - b.x) + abs(a.y - b.y); } void Manhattan_minimum_spanning_tree(int n, Point p[]) { int a[MAXN], b[MAXN]; tot = 0; for(int dir = 0; dir < 4; dir++) { if(dir == 1 || dir == 3) { for(int i = 0; i < n; i++) swap(p[i].x, p[i].y); } else if(dir == 2) { for(int i = 0; i < n; i++) p[i].x = -p[i].x; } sort(p, p + n, cmp); for(int i = 0; i < n; i++) a[i] = b[i] = p[i].y - p[i].x; sort(b, b + n); int m = unique(b, b + n) - b; for(int i = 1; i <= m; i++) bit[i].init(); for(int i = n - 1; i >= 0; i--) { int pos = lower_bound(b, b + m, a[i]) - b + 1; int ans = ask(pos, m); if(ans != -1) addedge(p[i].id, p[ans].id, dist(p[i], p[ans])); update(pos, p[i].x + p[i].y, i); } } memset(F, -1, sizeof(F)); sort(edge, edge + tot, cmpedge); total = 0; memset(head, -1, sizeof(head)); for(int i = 0; i < tot; i++) { int u = edge[i].u, v = edge[i].v; int t1 = find(u), t2 = find(v); if(t1 != t2) { F[t1] = t2; _addedge(u, v); _addedge(v, u); } } } int m; int a[MAXN]; struct Ans { long long a, b; } ans[MAXM]; long long temp ; int num[MAXN]; void add(int l, int r) { for(int i = l; i <= r; i++) { temp -= (long long)num[a[i]] * num[a[i]]; num[a[i]]++; temp += (long long)num[a[i]] * num[a[i]]; } } void del(int l, int r) { for(int i = l; i <= r; i++) { temp -= (long long)num[a[i]] * num[a[i]]; num[a[i]]--; temp += (long long)num[a[i]] * num[a[i]]; } } void dfs(int l1, int r1, int l2, int r2, int idx, int pre) { if(l2 < l1) add(l2, l1 - 1); if(r2 > r1) add(r1 + 1, r2); if(l2 > l1) del(l1, l2 - 1); if(r2 < r1) del(r2 + 1, r1); ans[pp[idx].id].a = temp - (r2 - l2 + 1); ans[pp[idx].id].b = (long long)(r2 - l2 + 1) * (r2 - l2); for(int i = head[idx]; i != -1; i = e[i].next) { int v = e[i].to; if(v == pre) continue; dfs(l2, r2, pp[v].x, pp[v].y, v, idx); } if(l2 < l1)del(l2, l1 - 1); if(r2 > r1)del(r1 + 1, r2); if(l2 > l1)add(l1, l2 - 1); if(r2 < r1)add(r2 + 1, r1); } long long gcd(long long a, long long b) { if(b == 0) return a; else return gcd(b, a % b); } int main() { while(scanf("%d%d", &n, &m) == 2) { for(int i = 1; i <= n; i++) scanf("%d", &a[i]); for(int i = 0; i < m; i++) { scanf("%d%d", &p[i].x, &p[i].y); p[i].id = i; pp[i] = p[i]; } Manhattan_minimum_spanning_tree(m, p); memset(num, 0, sizeof(num)); temp = 0; dfs(1, 0, pp[0].x, pp[0].y, 0, -1); for(int i = 0; i < m; i++) { long long d = gcd(ans[i].a, ans[i].b); printf("%lld/%lld ", ans[i].a / d, ans[i].b / d); } } return 0; }