• 怎么说呢,整理这个的目的就是为了有个简约的\(list\),方便以后查阅,复习起来不至于太吃力。
  • 并且……好像重温一遍所有,会更有一些新的认识。这也算是对我所学的一点整理了吧。

一、并查集的两种方式

其实就是一个随机化路径压缩,一个启发式合并。

  • 随机化路径压缩:这个地方就是由于f1 = Fa[f2]这句,如是写的人太多了,导致我造数据的时候稍微使一下坏,就可以让原来的好像十分和蔼可亲的\(\Theta(\alpha n) ≈\Theta(n)\)的并查集被卡成狗——毕竟每次合并,深度都起码会增加\(1\),我可以随随便便让其成为一条链。但如果我是随机合并的,就不存在被卡这种说法了233
  • 按秩合并(启发式合并\(\rm{Heuristic~Merge}\)): 这其实就是一个贪心,每次把深度小的合并到深度小的上,保证了最坏时间复杂度在\(O(n\log n)\)。关于这个复杂度,有一个比较有意思的证明。就是由于对于一个点,每次合并之后,其所在集合内的点数至少*2——不难发现这个结论是平凡的,因为我们每次是把深度较小的集合合并到深度较大的集合。那么也就是对于每个点,最多被拿去合并\(\log n\)次。所以总复杂度上界为\(O(n \log n)\)。
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <iostream> #define MAXN 200010 using namespace std ;
int N, M, A, B, C, F[MAXN] ; namespace _Heuristic{
int H[MAXN] ;
int find(int x){
if (x == F[x]) return x ;
return F[x] = find(F[x]) ;
}
inline void Merge(int x, int y){ // Heuristic Merge
int f1 = find(x), f2 = find(y) ; // Insert the less-height tree into the higher tree
if (H[f1] > H[f2]) F[f2] = f1 ; //because : H[x] < H[y] -> After Insert H[x] to H[y], H[y] ++ or H[y] == ;
else if (H[f1] < H[f2]) F[f1] = f2 ; //Therefore, we make the tree the lowest height under meaning .
else F[f1] = f2, ++ H[f2] ;
}
void solve2(){
fill(H, H + N + 2, 1) ;
while (M --){
scanf("%d%d%d", &A, &B, &C) ;
if (A == 2){
int f1 = find(B), f2 = find(C) ;
printf("%c\n", f1 == f2 ? 'Y' : 'N') ;
}
else Merge(B, C) ;
}
}
}
namespace _Compress{
int t ;
int find(int x){
if (x == F[x]) return x ;
return F[x] = find(F[x]) ;
}
void solve1(){
srand(time(NULL)) ;
while (M --){
scanf("%d%d%d", &A, &B, &C) ;
if (A == 2){
int f1 = find(B), f2 = find(C) ;
printf("%c\n", f1 == f2 ? 'Y' : 'N') ;
}
else {
int f1 = find(B), f2 = find(C) ;
if (!(t = rand() % 2)) F[f1] = f2 ; else F[f2] = f1 ;//随机化合并
}
}
}
}
int main(){
cin >> N >> M ;
for (int i = 1 ; i <= N ; ++ i) F[i] = i ;
if (M <= 50000) _Compress::solve1() ; else _Heuristic::solve2() ; return 0 ;
}

二、筛素数的两种方式

其实就是埃氏筛和线性筛啦~

关于埃氏筛复杂度的证明,大概就是每个数会被他所有的质因子都筛一遍,而质因子个数似乎是\(\ln \ln n\)个的,所以复杂度为\(O(n\log \log n)\)的(渐进意义下好像不区分对数底数)。

#include <bitset>
#include <cstdio>
#include <iostream>
#include <algorithm> using namespace std ;
int N, M ; namespace Es{ // Eratosthenes
#define MAXN 2000100
bool vis[MAXN] ; int A, i, j ;
void Ego(){
vis[1] = vis[0] = 1 ;
for (i = 2 ; i <= N ; ++ i){
if (vis[i]) continue ;
for (j = i + i ; j <= N ; j += i) vis[j] = 1 ;
}
}
void solve1(){
Ego() ;
while (M --)
scanf("%d", &A), printf("%s\n", !vis[A] ? "Yes" : "No") ;
}
#undef MAXN
}
namespace Euler{
#define MAXN 10001000
bitset <MAXN> vis ;
int Prime[MAXN], A, i, j, cnt ;
void Ego(){
vis[1] = vis[0] = 1 ;
for (i = 2 ; i <= N ; ++ i){
if (!vis[i]) Prime[++ cnt] = i ;
for (j = 1 ; j <= cnt ; ++ j){
if (i * Prime[j] > N) break ;
vis[i * Prime[j]] = 1 ;
if (!(i % Prime[j])) break ;
}
}
}
void solve2(){
Ego() ;
while (M --)
scanf("%d", &A), printf("%s\n", !vis[A] ? "Yes" : "No") ;
}
}
int main(){
cin >> N >> M ;
if (N <= 1000000) Es::solve1() ; else Euler::solve2() ; return 0 ;
}

三、最短路的两种方式

\(SPFA + SLF\)+乱搞

其实此处的\(SLF\)乱搞是借鉴的知乎上的一种方式,即在朴素的\(SLF\)下,再添加一个交换队头队尾的操作,具体来说:

  • \(\rm{SLF}\)优化:采用双端队列,每次拿自己的\(dist\)与队首元素的\(dist\)进行比较,如果自己比较近(\(dist\)较小),就放到队首;否则放到队尾。
  • 交换队首和队尾:每次\(SLF\)之前先按照\(SLF\)的方法把首尾操作一下。

但其实呢,无论如何\(SPFA\)的原理就是一个队列而已,我们做的一切无非就是让他看起来更像一个优先队列而不是\(FIFO\)的队列。所以……该被卡还是会被卡。

#include <deque>
#include <cstdio>
#include <iostream> #define R register
#define MAXN 200010
#define to(i) E[i].to
#define Inf 2147483647 using namespace std ;
deque <int> q ;
struct Edge{
int to, next, v ;
}E[MAXN] ; int head[MAXN], cnt ;
int N, M, S, dist[MAXN] ; bool vis[MAXN] ; inline int qr(){
int res = 0 ; char c = getchar() ;
while (!isdigit(c)) c = getchar() ;
while (isdigit(c)) res = (res << 1) + (res << 3) + c - 48, c = getchar() ;
return res ;
}
inline void Add(int u, int v, int w) {
E[++ cnt].to = v, E[cnt].v = w ;
E[cnt].next = head[u], head[u] = cnt ;
}
inline void SPFA(){
for (R int i = 1 ; i <= N ; ++ i) dist[i] = Inf ;
dist[S] = 0, q.push_back(S), vis[S] = 1 ;
while(!q.empty()){
R int now = q.front() ;
q.pop_front(), vis[now] = 0 ;
for (R int i = head[now] ; i ; i = E[i].next){
if (dist[to(i)] > dist[now] + E[i].v){
dist[to(i)] = dist[now] + E[i].v ;
if (!vis[to(i)]){
vis[to(i)] = 1 ;
R int F = q.front(), B = q.back() ;
if (dist[F] < dist[B]){
q.pop_front(), q.pop_back() ;
q.push_front(B), q.push_back(F) ;
if (dist[B] > dist[to(i)])
q.push_front(to(i)) ;
else q.push_back(to(i)) ;
}
else if (dist[F] > dist[to(i)])
q.push_front(to(i)) ;
else q.push_back(to(i)) ;
}
}
}
}
}
int main(){
cin >> N >> M >> S ; int A, B, C ;
for (R int i = 1 ; i <= M ; ++ i) A = qr(), B = qr(), C = qr(), Add(A, B, C) ;
SPFA() ; for (R int i = 1 ; i <= N ; ++ i) printf("%d ", dist[i]) ; return 0 ;
}

\(Heap + dijkstra\)

#include <deque>
#include <cstdio>
#include <iostream> #define R register
#define MAXN 200010
#define to(i) E[i].to
#define Inf 2147483647 using namespace std ;
deque <int> q ;
struct Edge{
int to, next, v ;
}E[MAXN] ; int head[MAXN], cnt ;
int N, M, S, dist[MAXN] ; bool vis[MAXN] ; inline int qr(){
int res = 0 ; char c = getchar() ;
while (!isdigit(c)) c = getchar() ;
while (isdigit(c)) res = (res << 1) + (res << 3) + c - 48, c = getchar() ;
return res ;
}
inline void Add(int u, int v, int w) {
E[++ cnt].to = v, E[cnt].v = w ;
E[cnt].next = head[u], head[u] = cnt ;
}
inline void SPFA(){
for (R int i = 1 ; i <= N ; ++ i) dist[i] = Inf ;
dist[S] = 0, q.push_back(S), vis[S] = 1 ;
while(!q.empty()){
R int now = q.front() ;
q.pop_front(), vis[now] = 0 ;
for (R int i = head[now] ; i ; i = E[i].next){
if (dist[to(i)] > dist[now] + E[i].v){
dist[to(i)] = dist[now] + E[i].v ;
if (!vis[to(i)]){
vis[to(i)] = 1 ;
R int F = q.front(), B = q.back() ;
if (dist[F] < dist[B]){
q.pop_front(), q.pop_back() ;
q.push_front(B), q.push_back(F) ;
if (dist[B] > dist[to(i)])
q.push_front(to(i)) ;
else q.push_back(to(i)) ;
}
else if (dist[F] > dist[to(i)])
q.push_front(to(i)) ;
else q.push_back(to(i)) ;
}
}
}
}
}
int main(){
cin >> N >> M >> S ; int A, B, C ;
for (R int i = 1 ; i <= M ; ++ i) A = qr(), B = qr(), C = qr(), Add(A, B, C) ;
SPFA() ; for (R int i = 1 ; i <= N ; ++ i) printf("%d ", dist[i]) ; return 0 ;
}

四、矩阵快速幂

主要是成员函数的写法,是从\(rqy\)的代码里抄来的。

#include <cstdio>
#include <cstring>
#include <iostream> #define LL long long
#define Mod 1000000007 using namespace std ;
LL N, K ;
struct Matrix{
LL M[200][200] ;
void clear() { memset(M, 0, sizeof(M)) ;}
void reset() {
clear() ;
for (int i = 1 ; i <= N ; ++ i) M[i][i] = 1 ;
}
Matrix friend operator *(const Matrix&A, const Matrix &B){
Matrix Ans ; Ans.clear() ;
for (int i = 1 ; i <= N; ++ i)
for (int j = 1 ; j <= N ; ++ j)
for (int k = 1 ; k <= N; ++ k)
Ans.M[i][j] = (Ans.M[i][j] + A.M[i][k] * B.M[k][j]) % Mod ;
return Ans ;
}
Matrix friend operator +(const Matrix&A, const Matrix &B){
Matrix Ans ; Ans.clear() ;
for (int i = 1 ; i <= N; ++ i)
for (int j = 1 ; j <= N ; ++ j)
Ans.M[i][j] = (A.M[i][j] + B.M[i][j]) % Mod ;
return Ans ;
}
} qwq, unit ;
inline Matrix expow(Matrix T, LL P){
Matrix Ans ; Ans.reset() ;
while (P){
if (P & 1) Ans = Ans * T ;
T = T * T, P >>= 1 ;
}
return Ans ;
}
int main(){
cin >> N >> K ;
for (int i = 1 ; i <= N ; ++ i)
for (int j = 1 ; j <= N ; ++ j)
cin >> qwq.M[i][j] ;
qwq = expow(qwq, K) ;
for (int i = 1 ; i <= N ; ++ i)
for (int j = 1 ; j <= N ; ++ j)
printf("%lld%c", qwq.M[i][j], " \n"[j == N]) ;
}

五、二分图匹配

虽然好像板子比较好写的样子……不过我还是觉得没准\(Dinic\)更好一些\(233\).

#include <cstdio>
#include <cstring>
#include <iostream> #define R register
#define MAXN 2000100
#define to(k) E[k].to using namespace std ;
struct Edge{
int to, next, v ;
}E[MAXN] ; int head[MAXN], Ans, cnt ;
int N, M, Ew, A, B, used[MAXN] ; bool vis[MAXN] ; inline void Add(int u, int v) {
if (u < 1 || v < 1 || u > N || v > M) return ;
E[++ cnt].to = v, E[cnt].next = head[u], head[u] = cnt ;
}
bool path(int u){
for (int k = head[u] ; k ; k = E[k].next){
if (!vis[to(k)]){
vis [to(k)] = 1 ;
if (!used[to(k)] || path(used[to(k)])){
used[to(k)] = u ;
return 1 ;
}
}
}
return 0 ;
}
int main(){
cin >> N >> M >> Ew ;
while(Ew --) scanf("%d%d", &A, &B), Add(A, B) ;
for (R int i = 1 ; i <= N ; ++ i)
memset(vis, 0, sizeof(vis)), Ans += (int)path(i) ;
cout << Ans << endl ; return 0 ;
}

\(\mathscr{To~Be~Continued....}\)

随机推荐

  1. SpringBoot —— AOP注解式拦截与方法规则拦截

    AspectJ是一个面向切面的框架,它扩展了Java语言.AspectJ定义了AOP语法,所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件. SpringBoot中AOP的使用 ...

  2. NIO学习笔记六:channel 之前数据传输及scatter/gather

    在Java NIO中,如果两个通道中有一个是FileChannel,那你可以直接将数据从一个channel传输到另外一个channel. FileChannel的transferFrom()方法可以将 ...

  3. SparseArray类

    继续阅读SparseArray的源码,从构造方法我们可以看出,它和一般的List一样,可以预先设置容器大小,默认的大小是10: public SparseArray() { this(10); } p ...

  4. JS中深浅拷贝 函数封装代码

    一.了解 基本数据类型保存在栈内存中,按值访问,引用数据类型保存在堆内存中,按址访问. 二.浅拷贝 浅拷贝只是复制了指向某个对象的指针,而不是复制对象本身,新旧对象其实是同一内存地址的数据,修改其中一 ...

  5. 使用openpyxl模块将Excel中的数据导入数据库

    这里将不介绍openpyxl模块的详细操作. 主要就是记录一个使用openpyxl模块将Excel表格的数据导入数据库中的实例. from openpyxl import load_workbook ...

  6. 用vue写一个仿app下拉刷新的组件

    如果你用vue弄移动端的页面,那么下拉刷新还是比较常见的场景,下面来研究如何写一个下拉刷新的组件(先上图); 由于节省大家的时间,样式就不贴出来了. html结构也不必介绍了,直接看代码吧-.- &l ...

  7. ActiveReports 报表应用教程 (10)---交互式报表之向下钻取(详细数据按需显示解决方案)

    在葡萄城ActiveReports报表中可以动态的显示或者隐藏某区域的数据,通过该功能用户可以根据需要显示或者隐藏所关心的数据,结合数据排序.过滤等功能可以让用户更方便地分析报表数据. 本文中展示的是 ...

  8. Java学习笔记之异常处理

    一.异常的分类 1.由Java虚拟机抛出的异常(Error):程序无法处理的问题,用户不用去进行处理(虚拟机错误丶内存溢出错误丶线程死锁) 2.Exception异常:程序本身可以进行处理的异常 1. ...

  9. cuda中threadIdx、blockIdx、blockDim和gridDim的使用

    threadIdx是一个uint3类型,表示一个线程的索引. blockIdx是一个uint3类型,表示一个线程块的索引,一个线程块中通常有多个线程. blockDim是一个dim3类型,表示线程块的 ...

  10. 如何在 Azure 门户中将托管数据磁盘附加到 Windows VM

    本文介绍了如何通过 Azure 门户将新的托管数据磁盘附加到 Windows 虚拟机. 在开始之前,请查看以下提示: 虚拟机的大小决定了可以附加多少个磁盘. 有关详细信息,请参阅虚拟机大小. 对于新磁 ...