├── .gitignore ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 binjie09 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # acm-template 2 | 3 | ## 模板编写规范说明 4 | 5 | - 使用markdown语法(便于赛前打印和生成目录) 6 | - 所有的算法模板都写在这一个md文件中 7 | - 代码名称 8 | - 需要有接口说明(可以看下面示例) 9 | - 代码最小原则,不添加任何不必要的变量和函数(最小不意味着最少,不要为了减短代码而去减短代码) 10 | - 代码块要使用如下的markdown包裹 11 | ``` 12 | ```c++ 13 | your code 14 | ``` 15 | ``` 16 | - 其他细则在实践过程中添加 17 | ## 快速幂 18 | 19 | 简介:介绍算法干啥,哪些情况用的上 20 | 21 | 22 | ### 非递归版算法 23 | 24 | 描述:简要描述算法实现框架(便于找错)。 25 | 26 | ​```c++ 27 | /* 28 | Version: 1.0 29 | Author: 徐祥昊 30 | Date: 2018.7.27 31 | Function List: 32 | 1.q_pow(__int64 a,__int64 b) 33 | a:这里是a参数的说明; 34 | b:这里是b参数的说明; 35 | ret:这里是返回值的说明; 36 | 2.some else func 37 | 3.some else func 38 | */ 39 | bool q_pow(__int64 a,__int64 b) 40 | { 41 | __int64 res=1,mod=b,carry=a; 42 | while(b) 43 | { 44 | if(b%2) 45 | { 46 | res=res*a%mod; 47 | } 48 | a=a*a%mod; 49 | b>>=1; 50 | } 51 | if(res == carry)return true; 52 | else return false; 53 | } 54 | ``` 55 | 56 | ## O(log(n)) 素数判定 57 | 目前已知的最快的单个素数判定方法 58 | 59 | ```c++ 60 | /* 61 | Version: 1.0 62 | Author: 王峰 63 | Date: 2018.8.26 64 | Function List: 65 | isPrime(long long n) 66 | 返回值为n是否是质数 67 | */ 68 | #include 69 | #include 70 | 71 | long long pow_mod(int a, long long d, long long mod) { 72 | long long res = 1, k = a; 73 | while(d) { 74 | if(d & 1) res = (res * k) % mod; 75 | k = k * k % mod; 76 | d >>= 1; 77 | } 78 | return res; 79 | } 80 | bool test(long long n, int a, long long d) { 81 | if(n == 2) return true; 82 | if(n == a) return true; 83 | if((n & 1) == 0) return false; 84 | while(!(d & 1)) d = d >> 1; 85 | long long t = pow_mod(a, d, n); 86 | while((d != n - 1) && (t != 1) && (t != n - 1)) { 87 | t = t * t % n; 88 | d <<= 1; 89 | } 90 | return (t == n - 1 || (d & 1) == 1); 91 | } 92 | bool isPrime(long long n) { 93 | if(n < 2LL) return false; 94 | int a[] = {2, 3, 61}; 95 | for (int i = 0; i <= 2; ++i) if(!test(n, a[i], n - 1)) return false; 96 | return true; 97 | } 98 | ``` 99 | 100 | ## 欧拉筛法求素数表 101 | 时间复杂度 O(n) 102 | 103 | ```c++ 104 | /* 105 | Version: 1.0 106 | Author: 王峰 107 | Date: 2018.8.26 108 | Function List: 109 | get_prime() 110 | check[i]表示i是否为合数 111 | prime[i]表示第i个质数,i从0开始计数 112 | */ 113 | #include 114 | #include 115 | 116 | const int MAXN = 10000001; 117 | const int MAXL = 10000001; 118 | int prime[MAXN]; 119 | int check[MAXL]; 120 | 121 | int get_prime() { 122 | int tot = 0; 123 | memset(check, 0, sizeof(check)); 124 | for (int i = 2; i < MAXL; ++i) { 125 | if (!check[i]){ 126 | prime[tot++] = i; 127 | } 128 | for (int j = 0; j < tot; ++j) { 129 | if (i * prime[j] > MAXL) { 130 | break; 131 | } 132 | check[i*prime[j]] = 1; 133 | if (i % prime[j] == 0) { 134 | break; 135 | } 136 | } 137 | } 138 | return tot; 139 | } 140 | ``` 141 | 142 | ## ST表 143 | 用于快速查询RMQ问题 建表时间复杂度O(log(n))空间复杂度O(log(n)) 查询时间复杂度O(1) 不可动态更改 144 | 145 | ```c++ 146 | /* 147 | Version: 1.0 148 | Author: 王峰 149 | Date: 2018.8.27 150 | Function List: 151 | init_table(n) n为原数组a的长度 152 | query_table(L, R) 查询原数组内区间[L,R]的最值 153 | */ 154 | #include 155 | #include 156 | 157 | using std::min; 158 | const int N = 1000010; 159 | 160 | int table[N][100]; 161 | int lg[N]; 162 | int a[N]; 163 | 164 | void init_table(int n) { 165 | for(int i=0; i(1< 195 | 196 | const int N = 1000010; 197 | int n; //实际数组的长度 198 | long long tree[4*N]; 199 | 200 | void add(int k, long long v) { 201 | while(k0) { 209 | ans+=tree[k]; 210 | k-=k&-k; 211 | } 212 | return ans; 213 | } 214 | ``` 215 | 216 | ## 求和线段树模板 217 | 218 | RMQ线段树小改即可 219 | 220 | 221 | ```c++ 222 | /* 223 | Function List: 224 | Build(1,n,1); 建树 225 | Add(L,C,1,n,1); 点更新 226 | Update(L,R,C,1,n,1); 区间更新 227 | ANS=Query(L,R,1,n,1); 区间查询 228 | */ 229 | 230 | typedef long long LL; 231 | 232 | const int MAXN=1e5+7; 233 | LL a[MAXN],ans[MAXN<<2],lazy[MAXN<<2]; 234 | 235 | void PushUp(int rt) { 236 | ans[rt]=ans[rt<<1]+ans[rt<<1|1]; 237 | } 238 | 239 | void Build(int l,int r,int rt) { 240 | if (l==r) { 241 | ans[rt]=0; 242 | return; 243 | } 244 | int mid=(l+r)>>1; 245 | Build(l,mid,rt<<1); 246 | Build(mid+1,r,rt<<1|1); 247 | PushUp(rt); 248 | } 249 | 250 | void PushDown(int rt,LL ln,LL rn) { 251 | if (lazy[rt]) { 252 | lazy[rt<<1]+=lazy[rt]; 253 | lazy[rt<<1|1]+=lazy[rt]; 254 | ans[rt<<1]+=lazy[rt]*ln; 255 | ans[rt<<1|1]+=lazy[rt]*rn; 256 | lazy[rt]=0; 257 | } 258 | } 259 | 260 | void Add(int L,int C,int l,int r,int rt) 261 | { 262 | if (l==r) 263 | { 264 | ans[rt]+=C; 265 | return; 266 | } 267 | int mid=(l+r)>>1; 268 | //PushDown(rt,mid-l+1,r-mid); 若既有点更新又有区间更新,需要这句话 269 | if (L<=mid) 270 | Add(L,C,l,mid,rt<<1); 271 | else 272 | Add(L,C,mid+1,r,rt<<1|1); 273 | PushUp(rt); 274 | } 275 | 276 | void Update(int L,int R,long long C,int l,int r,int rt) { 277 | if (L<=l&&r<=R) { 278 | ans[rt]+=C*(r-l+1); 279 | lazy[rt]+=C; 280 | return; 281 | } 282 | int mid=(l+r)>>1; 283 | PushDown(rt,mid-l+1,r-mid); 284 | if (L<=mid) Update(L,R,C,l,mid,rt<<1); 285 | if (R>mid) Update(L,R,C,mid+1,r,rt<<1|1); 286 | PushUp(rt); 287 | } 288 | 289 | LL Query(int L,int R,int l,int r,int rt) { 290 | if (L<=l&&r<=R) 291 | return ans[rt]; 292 | int mid=(l+r)>>1; 293 | PushDown(rt,mid-l+1,r-mid); 294 | LL ANS=0; 295 | if (L<=mid) ANS+=Query(L,R,l,mid,rt<<1); 296 | if (R>mid) ANS+=Query(L,R,mid+1,r,rt<<1|1); 297 | return ANS; 298 | } 299 | ``` 300 | 301 | ## 主席树 302 | 303 | 主席树:可持久化线段树, 在nlog(n)空内维护多个权值线段树 离线数据结构不支持修改 304 | 305 | 306 | ```c++ 307 | #include 308 | #include 309 | #include 310 | 311 | 312 | /* 313 | Version: 1.0 314 | Author: 王峰 315 | Date: 2018.9.25 316 | Function List: 317 | 本例为查询区间第K大,有一个基于排序去重的哈希,有时间分离出来单独成板. 318 | */ 319 | 320 | using std::lower_bound; 321 | using std::vector; 322 | 323 | const int maxn = 1e5+6; 324 | int cnt, root[maxn], a[maxn]; 325 | struct node{ 326 | int l, r, sum; 327 | }T[maxn*40]; 328 | vector v; 329 | 330 | int getid(int x) { 331 | return lower_bound(v.begin(), v.end(),x)-v.begin()+1; 332 | } 333 | 334 | void update(int l, int r, int &x, int y, int pos) { 335 | T[++cnt]=T[y],T[cnt].sum++,x=cnt; 336 | if(l==r)return ; 337 | int mid=(l+r)/2; 338 | if(mid>=pos)update(l, mid, T[x].l, T[y].l, pos); 339 | else update(mid+1, r, T[x].r, T[y].r, pos); 340 | } 341 | 342 | int query(int l, int r, int x, int y, int k) { 343 | if(l==r) return l; 344 | int mid = (l+r)/2; 345 | int sum = T[T[y].l].sum - T[T[x].l].sum; 346 | if(sum>=k)return query(l, mid, T[x].l, T[y].l, k); 347 | else return query(mid+1, r, T[x].r, T[y].r, k-sum); 348 | } 349 | 350 | int main() { 351 | int n, q; 352 | scanf("%d%d", &n, &q); 353 | for(int i=1; i<=n; i++) { 354 | scanf("%d", &a[i]); 355 | v.push_back(a[i]); 356 | } 357 | sort(v.begin(), v.end()),v.erase(unique(v.begin(), v.end()), v.end()); 358 | for(int i=1; i<=n; i++) update(1, n, root[i], root[i-1], getid(a[i])); 359 | for(int i=1; i<=q; i++) { 360 | int x, y, k; 361 | scanf("%d%d%d", &x, &y, &k); 362 | printf("%d\n", v[query(1,n,root[x-1],root[y],k)-1]); 363 | } 364 | return 0; 365 | } 366 | 367 | ``` 368 | 369 | ## dijkstra+priority_queue 370 | 371 | 用于求单源最短路径,不能处理负环 372 | ```c++ 373 | /* 374 | Version: 1.0 375 | Author: 王峰 376 | Date: 2018.9.3 377 | Function List: 378 | init(int n) 将带n个点的图初始化 379 | addEdge(int from, int to, int dist) 增加一条单向边 380 | dijkstra(int s) 求s为源点的最短路 381 | print(t, s) 打印从s为源点t为终点的一条路径 382 | */ 383 | #include 384 | #include 385 | #include 386 | #include 387 | 388 | using std::vector; 389 | using std::priority_queue; 390 | const int maxn = 1e6+7; 391 | const int INF = 0x3f3f3f3f; 392 | 393 | struct Edge { 394 | int from, to, dist; 395 | }; 396 | 397 | struct HeapNode { 398 | int d, u; 399 | bool operator < (const HeapNode& rhs) const { 400 | return d > rhs.d; 401 | } 402 | }; 403 | 404 | struct Dijkstra { 405 | int n, m; //点数和边数 406 | vector edges; //边列表 407 | vector G[maxn]; //每个节点出发的边编号, 从0开始编号 408 | bool vis[maxn]; //是否已经标记 409 | int d[maxn]; //s到各个点的距离 410 | //int p[maxn]; //最短路的上一条边 411 | 412 | void init(int n) { 413 | this->n = n; 414 | for(int i=0; i<=n; i++) { 415 | G[i].clear(); 416 | } 417 | edges.clear(); 418 | } 419 | 420 | /* 421 | void print(int t, int s) { 422 | int pre = edges[p[t]].from; 423 | if(t==s) { 424 | printf("%d ", s); 425 | return ; 426 | }else{ 427 | print(pre,s); 428 | printf("%d ", t); 429 | } 430 | } 431 | */ 432 | 433 | void addEdge(int from, int to, int dist) { 434 | edges.push_back((Edge){from, to, dist}); 435 | m = edges.size(); 436 | G[from].push_back(m-1); 437 | } 438 | 439 | void dijkstra(int s) { 440 | priority_queue Q; 441 | for(int i=0; i<=n; i++) d[i] = INF; 442 | d[s]=0; 443 | memset(vis, 0, sizeof(vis)); 444 | Q.push((HeapNode){0, s}); 445 | while(!Q.empty()) { 446 | HeapNode x = Q.top(); Q.pop(); 447 | int u = x.u; 448 | if(vis[u]) continue; 449 | vis[u] = true; 450 | for(int i=0; i d[u] + e.dist) { 453 | d[e.to] = d[u] + e.dist; 454 | //p[e.to] = G[u][i]; 455 | Q.push((HeapNode){d[e.to], e.to}); 456 | } 457 | } 458 | } 459 | } 460 | }D; 461 | ``` 462 | 463 | ## Bellman-Ford 464 | 单源最短路-可以判负环 队列实现,时间复杂度O(玄学) 465 | 466 | ```c++ 467 | /* 468 | Version: 1.0 469 | Author: 王峰 470 | Date: 2018.9.30 471 | Function List: the same as dijkstra algorithm 472 | */ 473 | #include 474 | #include 475 | #include 476 | #include 477 | 478 | using namespace std; 479 | 480 | const int maxn = 1e5+7; 481 | 482 | struct Edge { 483 | int from, to; 484 | double dist; 485 | }; 486 | 487 | struct BellmanFord { 488 | int n, m; 489 | vector edges; 490 | vector G[maxn]; 491 | bool inq[maxn]; 492 | double d[maxn]; 493 | int p[maxn]; 494 | int cnt[maxn]; 495 | 496 | void init(int n) { 497 | this->n = n; 498 | for(int i=0; i<=n; i++) { 499 | G[i].clear(); 500 | } 501 | edges.clear(); 502 | } 503 | 504 | void AddEdge(int from, int to, double dist) { 505 | edges.push_back((Edge){from, to, dist}); 506 | m = edges.size(); 507 | G[from].push_back(m-1); 508 | } 509 | 510 | bool negativeCycle() { 511 | queue Q; 512 | memset(inq, 0, sizeof(inq)); 513 | memset(cnt, 0, sizeof(cnt)); 514 | for(int i=1; i<=n; i++) { 515 | d[i]=0; 516 | inq[0]=true; 517 | Q.push(i); 518 | } 519 | while(!Q.empty()) { 520 | int u = Q.front(); 521 | Q.pop(); 522 | inq[u] = false; 523 | for(int i=0; i d[u] + e.dist) { 526 | d[e.to] = d[u] + e.dist; 527 | p[e.to] = G[u][i]; 528 | if(!inq[e.to]) { 529 | Q.push(e.to); 530 | inq[e.to] = true; 531 | if(++cnt[e.to] > n) return true; 532 | } 533 | } 534 | } 535 | } 536 | return false; 537 | } 538 | }; 539 | ``` 540 | 541 | ## hlpp 542 | 最大流-最强算法,时间复杂度O(|E|^2*sqrt(|V|)) 543 | 最高标号预流推进算法High Level Preflow Push 544 | 545 | ```c++ 546 | /* 547 | copy from https://blog.csdn.net/KirinBill/article/details/60882828 548 | 有时间的话参照算法自己实现一遍 549 | */ 550 | #include 551 | #include 552 | #include 553 | using namespace std; 554 | #define min(a,b) (a>1; 557 | int cnt,head[MAXN]; 558 | bool vis[MAXN]; 559 | 560 | struct line{ 561 | int to,next,cap; 562 | line(){ 563 | next=-1; 564 | } 565 | }edge[MAXM]; 566 | 567 | struct data{ 568 | int x,h,ef; 569 | friend bool operator< (const data &a,const data &b){ 570 | return a.h q; 575 | queue tmpq; 576 | 577 | inline void add(int u,int v,int w){ 578 | edge[cnt].next=head[u]; 579 | head[u]=cnt; 580 | edge[cnt].to=v; 581 | edge[cnt].cap=w; 582 | ++cnt; 583 | } 584 | 585 | void bfs(int t){ 586 | tmpq.push(t); 587 | vis[t]=true; 588 | int u,v; 589 | while(tmpq.size()){ 590 | u=tmpq.front(); 591 | for(int i=head[u];i!=-1;i=edge[i].next){ 592 | v=edge[i].to; 593 | if(!vis[v]){ 594 | node[v].h=node[u].h+1; 595 | tmpq.push(v); 596 | vis[v]=true; 597 | } 598 | } 599 | tmpq.pop(); 600 | } 601 | } 602 | 603 | int hlpp(int n,int s,int t){ 604 | for(int i=1;i<=n;++i) 605 | node[i].x=i; 606 | bfs(); 607 | data tmp; 608 | int u,v,f,tmph; 609 | for(int i=head[s];i!=-1;i=edge[i].next){ 610 | v=edge[i].to; 611 | edge[i^1].cap+=edge[i].cap; 612 | node[v].ef+=edge[i].cap; 613 | edge[i].cap=0; 614 | q.push(node[v]); 615 | } 616 | node[s].h=n; 617 | while(q.size()){ 618 | tmp=q.top(); 619 | q.pop(); 620 | u=tmp.x; 621 | tmph=INF; 622 | for(int i=head[u];i!=-1 && node[u].ef;i=edge[i].next){ 623 | v=edge[i].to; 624 | if(edge[i].cap){ 625 | if(node[u].h==node[v].h+1){ 626 | if(v!=s && v!=t && node[v].ef==0) 627 | q.push(node[v]); 628 | f=min(edge[i].cap,node[u].ef); 629 | edge[i].cap-=f; 630 | edge[i^1].cap+=f; 631 | node[u].ef-=f; 632 | node[v].ef+=f; 633 | } 634 | else tmph=min(tmph,node[v].h); 635 | } 636 | if(node[u].ef){ 637 | node[u].h=tmph+1; 638 | q.push(node[u]); 639 | } 640 | } 641 | return node[t].ef; 642 | } 643 | 644 | int main(){ 645 | memset(head,-1,sizeof(head)); 646 | int n,m,s,t,tmpu,tmpv,tmpw; 647 | scanf("%d%d%d%d",&n,&m,&s,&t); 648 | for(int i=1;i<=m;++i){ 649 | scanf("%d%d%d",&tmpu,&tmpv,&tmpw); 650 | add(tmpu,tmpv,tmpw); 651 | add(tmpv,tmpu,tmpw); 652 | } 653 | printf("%d",hlpp(n,s,t)); 654 | return 0; 655 | } 656 | ``` 657 | 658 | 659 | ## C组合数板子 660 | 661 | 时间复杂度O(2^n) 662 | 663 | ```c++ 664 | /* 665 | Version: 1.0 666 | Author: 王峰 667 | Date: 2018.9.17 668 | */ 669 | C[1][0] = C[1][1] = 1; 670 | for (int i = 2; i < N; i++){ 671 | C[i][0] = 1; 672 | for (int j = 1; j < N; j++) 673 | C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]); 674 | } 675 | ``` 676 | 677 | ## 容斥原理 678 | 679 | 时间复杂度O(n^2) 680 | 681 | ```c++ 682 | ll ans = 0; 683 | for(int i=0; i<(1<