首页 > 其他 > 详细

ACM模板整理

时间:2021-05-08 16:30:57      阅读:26      评论:0      收藏:0      [点我收藏+]

ACM模板整理

一.动态规划

  • 线性DP

    • LIS,LCS模型

      • LIS(最长上升子序列)

        • /*
          以"导弹拦截"为例
          第一问求最长单调不上升子序列 
          第二问求最长单调上升子序列
          O(nlogn)实现
          
          f[i]代表当最长单调子序列长度为i时最优的末尾元素
          len为最长单调子序列的长度
          以最长不上升子序列为例
          ①当a[i] <= f[len] 说明可以接在后面
          ②当a[i] > f[len] 在f中找到第一个小于a[i]的数 并替换为a[i]
          */
          
          #pragma GCC optimize(2)
          
          #include<bits/stdc++.h>
          //#define FAST ios::sync_with_stdio(false); cin.tie(0);
          //#define int long long
          
          using namespace std;
          
          const int MAXN = (int)1e5 + 5;
          
          int n, a[MAXN], f[MAXN];
          
          int Solve1() {
          	int len = 1;
          	f[1] = a[1];
          	for(int i = 2; i <= n; i++) {
          		if(a[i] <= f[len]) {
          			f[++len] = a[i];
          		} else {
          			f[upper_bound(f + 1, f + 1 + len, a[i], greater<int>() ) - f] = a[i];
          		}
          	}
          	return len;
          }
          
          int Solve2() {
          	int len = 1;
          	f[1] = a[1];
          	for(int i = 2; i <= n; i++) {
          		if(a[i] > f[len]) {
          			f[++len] = a[i];
          		} else {
          			f[lower_bound(f + 1, f + 1 + len, a[i]) - f] = a[i];
          		}
          	} 
          	return len;
          }
          
          signed main()
          {
          	n = 0;
          	while(cin >> a[++n]) ;
          	n--;
          	
          	for(int i = 1; i <= n; i++) cout << a[i] << " "; cout << endl;
          	
          	printf("%d\n", Solve1());
          	printf("%d", Solve2());
          	
          	return 0;
          }
          
          
          
      • LCS(最长公共子序列)

        • /*
          给定一个字符串 s 和一个字符串 t ,输出 s 和 t 的最长公共子序列。
          
          通过LCS的dp方程( O(n^2) ) 暴力寻找路径 ( O(n^2) )
          */
          
          #pragma GCC optimize(2)
          
          #include<bits/stdc++.h>
          //#define FAST ios::sync_with_stdio(false); cin.tie(0);
          //#define int long long
          
          using namespace std;
          
          const int MAXN = 3005;
          
          struct Node {
          	int x, y;
          };
          
          char S[MAXN], T[MAXN], ans[MAXN];
          int sLen, tLen, dp[MAXN][MAXN];
          Node pre[MAXN][MAXN];
          
          signed main()
          {
          	scanf("%s", S + 1);
          	scanf("%s", T + 1);
          	int sLen = strlen(S + 1);
          	int tLen = strlen(T + 1);
          	
          	for(int i = 1; i <= sLen; i++) {
          		for(int j = 1; j <= tLen; j++) {
          			if(S[i] == T[j]) {
          				dp[i][j] = dp[i - 1][j - 1] + 1;
          			} else {
          				dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
          			}
          		}
          	}
          	/*
          	for(int i = 1; i <= sLen; i++) {
          		for(int j = 1; j <= tLen; j++) {
          			printf("%d ", dp[i][j]);
          		}
          		printf("\n");
          	}
          	*/
          	int p = 0, px = sLen, py = tLen;
          	while(px != 0 && py != 0) {
          		if(dp[px][py] == dp[px][py - 1]) py--;
          		else if(dp[px][py] == dp[px - 1][py]) px--;
          		else if(dp[px][py] == dp[px - 1][py - 1] + 1) {
          			ans[++p] = S[px];
          			px--;
          			py--;
          		}
          	}
          	
          	for(int i = p; i >= 1; i--) printf("%c", ans[i]);
          	
          	return 0;
          }
          
          
      • LCIS(最长公共上升子序列)

        • #pragma GCC optimize(2)
          
          #include<bits/stdc++.h>
          //#define FAST ios::sync_with_stdio(false); cin.tie(0);
          //#define int long long
          
          using namespace std;
          
          const int INF = 0x3f3f3f3f;
          const int MAXN = 3005;
          
          int n, a[MAXN], b[MAXN], dp[MAXN][MAXN];
          /*
          dp[i][j]表示
          	a数组前i个元素和b数组前j个元素
          	以b[j]为结尾
          的LCIS
          */ 
          
          void LCIS() {
          	int curmax;
          	//O(n^2)做法
          	for(int i = 1; i <= n; i++) {
          		curmax = dp[i][0] + 1;
          		for(int j = 1; j <= n; j++) {
          			if(a[i] == b[j]) {
          				dp[i][j] = max(dp[i][j], curmax);
          			} else {
          				dp[i][j] = dp[i - 1][j];
          			}
          			if(b[j] < a[i])	//等价于朴素做法中b[k] < b[j] (b[j] == a[i])
          				curmax = max(curmax, dp[i - 1][j] + 1);
          		}
          	}
          	
          	/*
          	//O(n^3)朴素做法 不采用
          	for(int i = 1; i <= n; i++) {
          		for(int j = 1; j <= n; j++) {
          			if(a[i] == b[j]) {
          				for(int k = 0; k < j; k++) {
          					if(b[k] < b[j]) {
          						dp[i][j] = max(dp[i][j], dp[i - 1][k] + 1);
          					}
          				}
          			} else {
          				dp[i][j] = dp[i - 1][j];
          			}
          		}
          	}
          	*/
          }
          
          signed main()
          {
          	scanf("%d", &n);
          	for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
          	for(int i = 1; i <= n; i++) scanf("%d", &b[i]);
          	
          	LCIS();
          	
          	int ans = 0;
          	for(int i = 0; i <= n; i++) {
          		for(int j = 0; j <= n; j++) {
          			ans = max(ans, dp[i][j]);
          		}
          	}
          	printf("%d", ans);
          	return 0;
          }
          
          /*
          input1:
          4
          2 2 1 3
          2 1 2 3
          
          output1:
          2
          
          input2:
          10
          1 5 3 6 3 2 7 3 6 2 
          9 6 2 3 1 5 3 3 6 1
          
          output2:
          3
          */
          
          
    • 背包模型

      • 
        
    • 最大子段和

      • 在数列的一维方向找到一个连续的子数列,使该子数列的和最大

      • /*
        这种问题有两种解法: 
        1.分治+前缀和 O(nlogn) 
        2.DP O(n) 
        代码采用第二种方法 
        */
        
        #pragma GCC optimize(2)
        
        #include<bits/stdc++.h>
        //#define FAST ios::sync_with_stdio(false); cin.tie(0);
        //#define int long long
        
        using namespace std;
        
        const int MAXN = (int)2e5 + 5;
        const int INF = 0x7fffffff;
        
        int n, a[MAXN], dp[MAXN];
        
        signed main()
        {
        	scanf("%d", &n);
        	for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        	
        	for(int i = 1; i <= n; i++) {
        		/*
        		dp[i]代表最大子段和的尾部元素 
        		扫到i时 能转移过来的状态只有两种
        		一是另起炉灶 只留有a[i] 
        		二是将a[i]接到a[i-1] 此时最大子段和就是dp[i-1] + a[i] 
        		*/ 
        		dp[i] = max(dp[i - 1] + a[i], a[i]);
        	}
        	
        	int ans = -INF;
        	for(int i = 1; i <= n; i++) ans = max(ans, dp[i]);
        	printf("%d", ans);
        	
        	return 0;
        }
        
  • 数位DP

    • //通用模板
      
      dfs(数的最后若干位,各种限制条件,当前第几位)
      	if 最后一位
          	return 各种限制条件下的返回值
          局部变量 curDigit=当前位的数字
          局部变量 sum=0;
          for i=0 to curDigit-1
          	sum+=当前位取i时一定无无限制的合法状态数
              sum+=当前位取i时满足当前限制的合法状态数
          根据curDigit更新限制条件 不再满足则return sum
          return sum+dfs(当前位后的若干位,更新后的限制条件,下一位)
      
      solve(当前数)
      	if(只有一位) return 对应的贡献
          局部变量 maxDigit;
          for maxDigit = 当前数可能最高位 to 1
          	if 当前位有数字 break
          局部变量 curDigit=当前位数字
          局部变量 sum=0
          for i=1 to curDigit-1
          	sum+=当前位取i后合法情况任意取的贡献
          for i=1 to maxDigit-1
          	for j=1 to 9
              	sum+=第i位取j后合法情况任意取的贡献
          sum+=dfs(去掉第一位后的若干位,限制条件,第二位)
          return sum
      
      main
      	预处理当前位取i的各种条件各种限制的贡献
          读入 L R
          --L
          输出 solve(R)-solve(L)
          return 0
      
    • [CQOI2016]手机号码为例

    • 人们选择手机号码时都希望号码好记、吉利。比如号码中含有几位相邻的相同数字、不含谐音不吉利的数字等。手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售。为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数量。

      工具需要检测的号码特征有两个:号码中要出现至少 3 个相邻的相同数字;号码中不能同时出现 8 和 4。号码必须同时包含两个特征才满足条件。满足条件的号码例如:13000988721、23333333333、14444101000。而不满足条件的号码例如:1015400080、10010012022。

      手机号码一定是 11 位数,前不含前导的 0。工具接收两个数 L 和 R,自动统计出 [L,R] 区间内所有满足条件的号码数量。L 和 R 也是 11 位的手机号码。

    • #include<bits/stdc++.h>
      #define FAST ios::sync_with_stdio(false); cin.tie(0);
      #define int long long
      
      using namespace std;
      
      int dp[20][3][3][3][20][20][3], num[30], l, r;
      
      int f(int len, bool three, bool exist8, bool exist4, int pre1, int pre2, int limit) {
      	//printf("%d %d %d %d %d %d %d\n", len, three, exist8, exist4, pre1, pre2, limit);
      	/*
      	three 是否存在3个相邻的相同数字 
      	exist8 是否存在8 
      	exist4 是否存在4 
      	pre1 前推一位数位 
      	pre2 前推两位数位 
      	limit 有无数位限制(len位以后的数字可以随便取) 
      	*/
      	if(exist4 && exist8) return 0;
      	if(len == 0) return three;
      	if(~dp[len][three][exist8][exist4][pre1][pre2][limit]) //记忆化 
      		return dp[len][three][exist8][exist4][pre1][pre2][limit];
      	int res = 0;
      	int digit = limit == 0 ? 9 : num[len];
      	for(int i = 0; i <= digit; i++) {
      		//printf("%d %d %d %d %d %d %d\n", len - 1, i == pre1 && pre1 == pre2, exist8 || i == 8, exist4 || i == 4, i, pre1, limit && i == num[len]);
      		res += f(len - 1, three || ((i == pre1) && (pre1 == pre2)), exist8 || (i == 8), exist4 || (i == 4), i, pre1, limit && (i == num[len]));
      	}
      	return dp[len][three][exist8][exist4][pre1][pre2][limit] = res;
      }
      
      int Solve(int x) {
      	int len = 0;
      	memset(dp, -1, sizeof(dp));
      	while(x > 0) {
      		num[++len] = x % 10;
      		x /= 10;
      	}
      	if(len != 11) return 0;
      	int res = 0;
      	//for(int i = 1; i <= len; i++) cout << num[i] << " "; cout << endl;
      	for(int i = 1; i <= num[len]; i++) {
      		res += f(10, 0, i == 8, i == 4, i, 0, i == num[len]);
      	}
      	return res;
      }
      
      signed main()
      {
      	FAST 
      	cin >> l >> r;
      	cout << Solve(r) - Solve(l - 1);
      	return 0;
      }
      

二.字符串

  • 字符串hash

  • KMP

    • #include<bits/stdc++.h>
      
      using namespace std;
      
      const int MAXN = 1e6 + 5;
      
      char S1[MAXN], S2[MAXN];
      int l1, l2, pmt[MAXN];	//pmt数组(部分匹配表) 向右偏移一位为next数组 并让next[0] = -1 
      
      void KMP() {
      	pmt[0] = 0;
      	int p = 0;
      	for(int i = 2; i <= l2; i++) {
      		while(p && S2[p + 1] != S2[i]) p = pmt[p];
      		if(S2[p + 1] == S2[i]) p++;
      		pmt[i] = p;
      	}
      	p = 0;
      	for(int i = 1; i <= l1; i++) {
      		while(p && S2[p + 1] != S1[i]) p = pmt[p];
      		if(S2[p + 1] == S1[i]) p++;
      		if(p == l2) {
      			printf("%d\n", i - p + 1);
      			p = pmt[p];
      		}
      	}
      }
      
      int main()
      {
      	cin >> S1 + 1;
      	cin >> S2 + 1;
      	l1 = strlen(S1 + 1);
      	l2 = strlen(S2 + 1);
      	KMP();
      	for(int i = 1; i <= l2; i++) printf("%d ", pmt[i]);
      	return 0;
      }
      
  • Trie

  • Manacher

    • /*
      Manacher算法:
      先在两个字符串中插入某个字符(‘$‘) 避免分别处理奇回文和偶回文的情况
      设置两个指针maxR(前i个字符能回文扩展到的最右端) pos(前i个字符中哪个字符能回文扩展到最右端)
      
      每次扫描到第i个字符时 
      ①如果i<maxR,更新f[i] ②暴力拓展maxR(maxR从起点到终点且不会往回退) ③maxR增大时更新maxR和pos
      */
      
      #include<bits/stdc++.h>
      
      using namespace std;
      
      const int MAXN = (int)1.1e7 + 5;
      
      char S[MAXN << 1], T[MAXN << 1];
      int f[MAXN << 1], n;
      
      void Manacher() {
      	//Prework
      	T[0] = ‘#‘;
      	T[1] = ‘$‘;
      	for(int i = 1; i <= n; i++) {
      		T[i * 2] = S[i];
      		T[i * 2 + 1] = ‘$‘;
      	}
      	n = n * 2 + 1;
      	for(int i = 0; i <= n; i++) S[i] = T[i];
      	
      	//Manacher
      	int maxR = 0, pos = 0;
      	for(int i = 1; i <= n; i++) {
      		if(i < maxR) f[i] = min(f[pos * 2 - i], maxR - i);
      		while(S[i + f[i] + 1] == S[i - f[i] - 1] && i - f[i] - 1 > 0 && i + f[i] + 1 <= n) f[i]++;
      		if(i + f[i] > maxR) maxR = i + f[i], pos = i;
      	}
      }
      
      void PrintTest() {
      	for(int i = 1; i <= n; i++) cout << f[i] << " "; cout << endl;
      }
      
      int main()
      {
      	//ios::sync_with_stdio(false);
      	scanf("%s", S + 1);
      	n = strlen(S + 1);
      	Manacher();
      	int ans = 0;
      	for(int i = 1; i <= n; i++) {
      		ans = max(ans, f[i]);
      	}
      	printf("%d", ans);
      	return 0;
      }
      
      
  • AC自动机

    • 
      
  • exKMP

    • 
      

三.数据结构

  • 单调队列和单调栈

    • 单调队列(滑动窗口)

    • /*
      可采用STL的deque实现 但常数过大 不推荐使用 
      */
      
      #pragma GCC optimize(2)
      
      #include<bits/stdc++.h>
      //#define FAST ios::sync_with_stdio(false); cin.tie(0);
      //#define int long long
      
      using namespace std;
      
      const int MAXN = (int)1e6 + 5;
      
      int n, k, a[MAXN];
      int Index[MAXN], Value[MAXN];	//单调队列 
      
      void QueryMin() {
      	int head = 1, tail = 0;
      	for(int i = 1; i <= n; i++) {
      		while(head <= tail && Index[head] < i - k + 1) head++;
      		while(head <= tail && Value[tail] > a[i]) tail--;	//保持单调队列始终升序 
      		Index[++tail] = i;
      		Value[tail] = a[i];
      		if(i >= k) printf("%d ", Value[head]);
      	}
      	printf("\n");
      }
      
      void QueryMax() {
      	int head = 1, tail = 0;
      	for(int i = 1; i <= n; i++) {
      		while(head <= tail && Index[head] < i - k + 1) head++;
      		while(head <= tail && Value[tail] < a[i]) tail--;	//保持单调队列始终降序 
      		Index[++tail] = i;
      		Value[tail] = a[i];
      		if(i >= k) printf("%d ", Value[head]); 
      	}
      	printf("\n");
      }
      
      signed main()
      {
      	scanf("%d%d", &n, &k);
      	for(int i = 1; i <= n; i++) {
      		scanf("%d", &a[i]);
      	}
      	QueryMin();
      	memset(Index, 0, sizeof(Index));
      	memset(Value, 0, sizeof(Value));
      	QueryMax();
      	return 0;
      }
      
    • 单调栈

    • 定义函数 \(f(i)\) 代表数列中第 i 个元素之后第一个大于\(a_i\)的元素的下标\(f(1...n)\) (f数组)

    • #include<bits/stdc++.h>
      
      using namespace std;
      
      const int MAXN = (int)3e6 + 5;
      
      int n, a[MAXN], f[MAXN];
      int Value[MAXN], Index[MAXN];
      
      void Query() {
      	int top = 0;
      	for(int i = 1; i <= n; i++) {
      		while(top > 0 && Value[top] < a[i]) {	//单调栈保持降序 
      			f[Index[top]] = i;
      			top--;
      		}
      		Value[++top] = a[i];
      		Index[top] = i;
      	} 
      }
      
      int main()
      {
      	scanf("%d", &n);
      	for(int i = 1; i <= n; i++) {
      		scanf("%d", &a[i]);
      	}
      	
      	Query();
      	
      	for(int i = 1; i <= n; i++) {
      		printf("%d ", f[i]);
      	}
      	return 0;
      }
      
      /*
      input:
      5
      1 4 2 3 5
      
      output:
      2 5 4 5 0
      */
      
  • 最近公共祖先(LCA)

    • 
      
  • ST表和RMQ问题

    • #include<bits/stdc++.h>
      
      using namespace std;
      
      const int MAXN = (int)1e5 + 5;
      
      int n, m, a[MAXN];
      int st[MAXN << 1][45];  //注意st表第一维空间(n的数量)至少要开2倍
      
      void Prework() {
      	for(int k = 1; (1 << k) <= n; k++) {
      		for(int i = 1; i <= n; i++) {
      			st[i][k] = max(st[i][k - 1], st[i + (1 << (k - 1) )][k - 1]);
      		}
      	} 
      	/*
      	for(int i = 1; i <= n; i++) {
      		for(int j = 1; (1 << j) <= n; j++) {
      			cout << st[i][j] << " ";
      		}
      		cout << endl;
      	}
      	*/
      }
      
      inline int Query(int l, int r) {
      	int t = floor(log2(r - l + 1));
      	return max(st[l][t], st[r - (1 << t) + 1][t]);
      }
      
      int main() 
      {
      	scanf("%d%d", &n, &m);
      	for(int i = 1; i <= n; i++) {
      		scanf("%d", &a[i]);
      		st[i][0] = a[i];
      	}
      	
      	Prework();
      	
      	while(m--) {
      		int l, r;
      		scanf("%d%d", &l, &r);
      		printf("%d\n", Query(l, r));
      	}
      }
      
  • 树状数组

    • /*
      区间查询 单点更新
      */
      
      #include<bits/stdc++.h>
      #define MAXN (int)1e6 + 7
      #define ll long long
      
      using namespace std;
      
      ll n, q, a[MAXN], c[MAXN];
      
      ll lowbit(ll x) {
      	return x & (-x);
      }
      
      void Modify(int t, int x) {
      	while(t <= n) {
      		c[t] += x;
      		t += lowbit(t);
      	}
      }
      
      ll Query(int t) {
      	ll sum = 0;
      	while(t > 0) {
      		sum += c[t];
      		t -= lowbit(t);
      	}
      	return sum;
      }
      
      int main(){
      	scanf("%d%d", &n, &q);
      	for(int i = 1; i <= n; i++) {
      		scanf("%d", &a[i]);
      		Modify(i, a[i]);
      	}
      	while(q--) {
      		int opt, x, y;
      		scanf("%d%d%d", &opt, &x, &y);
      		if(opt == 1) {
      			Modify(x, y);	//a[x]加上y
      		} else {
      			printf("%d\n", Query(y) - Query(x - 1));	//[x,y]的区间和
      		}
      	}
      	return 0;
      }
      
    • 将原数组化为差分数组 对于原数组的区间更新 单点查询 可化为对于差分数组的单点更新(两次) 区间查询(差分数组的前缀和)

    • 树状数组求逆序对(有重复元素):

      • 逆序对的基本思想是对于每一个下标i,找下标在[1,i-1]中比a[i]大的数

      • 排序之后按值从大到小,往树状数组相应下标处依次加入元素 每次先查询前面有多少个比当前元素大的元素

      • 注意:当出现相同元素时 我们要按下标从大到小排序 这样查询的时候相同元素会按下标从后往前查询 这样就防止逆序对出现下标不同而值相同的情况

      • #include<bits/stdc++.h>
        #define MAXN (int)1e6 + 7
        #define int long long
        
        using namespace std;
        
        struct Node {
        	int val, num;
        }a[MAXN];
        
        int n, c[MAXN];
        
        bool cmp(Node a, Node b) {
        	if(a.val == b.val) return a.num > b.num;
        	return a.val > b.val;
        }
        
        int lowbit(int x) {
        	return x & (-x);
        }
        
        void Modify(int t, int x) {
        	while(t <= n) {
        		c[t] += x;
        		t += lowbit(t);
        	}
        }
        
        int Query(int t) {
        	int sum = 0;
        	while(t > 0) {
        		sum += c[t];
        		t -= lowbit(t);
        	}
        	return sum;
        }
        
        signed main(){
        	scanf("%lld", &n);
        	for(int i = 1; i <= n; i++) {
        		scanf("%lld", &a[i].val);
        		a[i].num = i;
        	}
        	sort(a + 1, a + 1 + n, cmp);
        	int ans = 0;
        	for(int i = 1; i <= n; i++) {
        		Modify(a[i].num, 1);
        		ans += Query(a[i].num - 1);
        	}
        	printf("%lld", ans);
        	return 0;
        }
        
  • 线段树

    • 
      
  • 树链剖分

    • 
      
  • 扫描线

    • 
      
  • 平衡二叉树

    • AVL(不常用)

      • 
        

四.图论

  • 最短路

    • Dijkstra

    • Floyd

    • SPFA(死了)

五.数学

  • 欧拉筛

    • #include<bits/stdc++.h>
      
      using namespace std;
      
      const int maxn = 1e7 + 5;
      
      int n, m, isprime[maxn], prime[maxn], cnt;
      bool ans[maxn];
      
      void PreWork() {
      	cnt = 0;
      	isprime[1] = 1;
      	for(int i = 2; i <= n; i++) {
      		if(!isprime[i]) {
      			prime[++cnt] = i;
      			ans[i] = 1;
      		}
      		for(int j = 1; j <= cnt && i * prime[j] <= n; j++) {
      			isprime[i * prime[j]] = 1;
      			if(i % prime[j] == 0) break;
      		}
      	}
      	//for(int i = 1; i <= cnt; i++) cout << prime[i] << " "; cout << endl;
      }
      
      
      int main()
      {
      	scanf("%d%d", &n, &m);
      	PreWork();
      	for(int i = 1; i <= m; i++) {
      		int now;
      		scanf("%d", &now);
      		if(ans[now]) printf("Yes");
      		else printf("No");
      		printf("\n");
      	}
      	return 0;
      }
      
  • 组合数各种处理

    • 
      
  • 求逆元

    • 
      
  • 有理数取模

    • 给出一个有理数 \(c=\frac{a}{b}\),求 \(c\bmod 19260817\)的值。

    • 
      

六.其他算法

七.杂项

  • STL部分(摘自jvxie.com)

    • 万能(误)算法头文件(部分)

      • #include <algorithm>
        using namespace std; 
        int main() {
            iterator begin, end; // 指代某种数据结构首尾迭代器
            T i, x, a, b;
            
            sort(begin, end, <cmp>); // 排序函数,默认从小到大
            // 遇到需要特殊排序的需要编写cmp函数 or 重载内部运算符
            next_permutation(begin, end); // 下一个排列
            prev_permutation(begin, end); // 前一个排列
            
            set_union(begin(a), end(a), begin(b), end(b), begin(c));
            // 取两个有序序列a、b的并集,存放到c中
            set_intersection(begin(a), end(a), begin(b), end(b), begin(c));
            // 取两个有序序列a、b的交集,存放到c中
            set_difference(begin(a), end(a), begin(b), end(b), begin(c));
            // 取两个有序序列a、b的差集,存放到c中
            unique(begin, end); // 有序数据去重
            merge(begin(a), end(a), begin(b), end(b), begin(c), cmp);
            // 合并两个有序序列a、b,存放到c中,cmp可定义新序列排列方式
            
            lower_bound(begin, end, x); // 返回x的前驱迭代器
            // 在普通的升序序列中,x的前驱指的是第一个大于等于x的值
            upper_bound(begin, end, x); // 返回x的后继迭代器
            // 在普通的升序序列中,x的后继指的是第一个大于x的值
            // 上述两个函数时间复杂度为O(log2(n)),内部实现是二分
            // 如果找不到这样的值,会返回end
            
            find(begin, end, x); // O(n)查找x
            binary_search(begin, end, x) // 二分查找x,返回bool
            min(a, b); max(a, b); // 返回a、b中的最小/最大值
            fill(begin, end, x); // 往容器的[begin, end)内填充x
            swap(a, b); // 交换a、b的值
            
            return 0;
        }
        
    • 动态数组(vector)、双向链表(list)

      • #include <vector>
        #include <list>
        using namespace std;
        int main() {
            T i;
            unsigned int n, x;
            bool flag;
            iterator it;
            
            // 动态数组部分
            // 注意vector的空间需要预留两倍大小
            vector<T> v;
            v.push_back(i); // 往数组尾添加一个元素i
            v[x]; // 访问第x - 1个元素
            v.begin(); // 返回头元素的迭代器
            v.end(); // 返回末尾迭代器(尾元素的下一个)
            n = v.size(); // 数组中元素数量
            v.pop_back(); // 删除最后一个元素
            v.erase(it);  // 删除某个的元素
            v.insert(x, i);  // 在x位置插入元素i
            // erase、insert时间复杂度为O(n)
            v.clear(); // 清空数组,不释放空间
            flag = v.empty(); // 判断数组是否为空(真值)
            
            // 链表部分
            list<T> li;
            li.push_front(i); // 在链头添加一个元素i
            li.push_back(i); // 在链尾添加一个元素i
            li.pop_front(i); // 删除链表头元素
            li.pop_back(i); // 删除链表尾元素
            li.erase(it);  // 删除某个的元素
            li.insert(x, i);  // 在x位置插入元素i O(n)
            li.begin(); // 返回头元素的迭代器
            li.end(); // 返回末尾迭代器(尾元素的下一个)
            n = li.size(); // 链表中元素数量
            li.remove(i); // 删除链表中所有值为i的元素
            li.unique(); // 移除所有连续相同元素,留下一个
            li.reverse(); // 反转链表
            li.clear(); // 情况链表,不释放空间
            
            return 0;
        }
        
    • 普通队列、双端队列、优先队列

      • #include <queue> // 队列头文件
        #include <deque> // 双端队列头文件
        using namespace std;
        int main() {
            T i; 
            unsigned int n, x; 
            bool flag;
            
            // 普通队列部分,注意queue没有迭代器
            queue<T> q, tmp_q;  // 定义普通队列
            q.push(i); // 队尾插入元素i
            q.pop();   // 弹出队首元素
            i = q.front(); // 访问队首元素
            i = q.back();  // 访问队尾元素
            n = q,size();  // 队内元素数量
            flag = q.empty(); // 判断队列是否为空(真值)
            q.swap(tmp_q); // 交换两个队列元素
            
            // 优先队列部分,注意其没有迭代器
            priority_queue<T> pq; // 定义优先队列
            pq.push(i); // 队尾插入元素i
            pq.pop();   // 弹出队首元素
            i = pq.top(); // 访问队首元素
            n = q,size();  // 队内元素数量
            flag = q.empty(); // 判断队列是否为空(真值)
            q.swap(tmp_q); // 交换两个队列元素
            // 注意优先队列内部是使用<运算符,默认大根堆
            // 可以采用重载运算符或加入运算符类自定义排列方式
            // 例:priority_queue<T, vector<T>, greater<T> > 小根堆
            /*
                struct node {
                    int x, y;
                };
                bool operator < (node a, node b) {
                    // 这里注意是<右边的元素会放在前面
                    if(a.x != b.x) return a.x < b.x;
                    else return a.y < b.y;
                }
                priority_queue<node>
            */
            
            // 双端队列部分
            // 注意deque用到了map来映射,时间复杂度上常数略大
            deque<T> dq; // 定义双端队列
            // 可以称为vector、list、queue的结合体
            // 用法类似,这里只给代码不做注释
            dq.push_back(i);
            dq.push_front(i);
            dq.front();
            dq.back();
            dq.pop_front();
            dq.pop_back();
            dq.begin();
            dq.end();
            dq[x];
            n = dq.size();
            flag = dq.empty();
            dq.insert(x, i);
            
            return 0;
        }
        
      • #include <stack>
        using namespace std;
        int main() {
            T i;
            unsigned int n;
            bool flag;
            
            stack<T> st; // 注意stack没有迭代器
            st.push(i); // 往栈顶加入一个元素
            st.pop(); // 弹出栈顶元素
            i = st.top(); // 获得栈顶元素的值
            flag = st.empty(); // 判断是否为空(真值)
            n = st.size(); // 获得栈内元素个数
            
            return 0;
        }
        
    • pair(成组)、set(有序元素序列)

      • #include <set>
        #include <pair>
        using namespace std;
        int main() {
            T i;
            T1 t1;
            T2 t2;
            iterator it;
            unsigned int n;
            bool flag;
            
            // pair是将两种元素组成一对
            pair<T1, T2> p;
            p = make_pair(t1, t2); // 将t1、t2构造成一对
            // pair支持比较,遵循字典序
            p.first;  // 访问第一个元素,这里是t1
            p.second; // 访问第二个元素,这里是t2
            
            // set内部是RB-tree维护
            set<int> st; // 注意,set内元素不重复
            st.insert(i); // 往set内插入一个元素i
            	// 时间复杂度O(log2(n)) 这里会返回一个<pair>迭代器
            	// first指向插入元素后所在的迭代器
            	// second指向是否插入成功(真值)
            st.begin(); // 返回首迭代器
            st.end(); // 范围尾迭代器
            st.erase(it); st.erase(i);
            	// 删除某个元素
            st.equal_range(i); // 返回几何中与i相等的上下限两个迭代器
            flag = st.empty(); 
            n = st.size();
            st.clear();
            // set内置了lower_bound和upeer_bound函数
            // 用法和algorithm的一样
            
            // 可重复元素set
            multiset<int> mst;
            // 用法与set大致相同
            // 唯一不同只在删除函数上
            mst.erase(i); // 会删除所有值为i的元素
            
            return 0;
        }
        
        // 如果需要给set自定义排序顺序
        struct CMP {
            bool operator() (const int& a, const int& b) const {
                return a > b; // 返回真值则代表左边的值优先级高
            }
        };
        multiset<int, CMP> mst;
        
    • map(映射)

      • #include <map>
        using namespace std;
        int main() {
            T1 t1;
            T2 t2;
            
            // map将两种元素做映射,一种指向另一种
            // 内部也是RB-tree维护
            map<T1, T2> mp;
            mp[t1] = t2; // 直接让t1对应到t2
           	mp[t1]; // 访问t1对应的内容,时间复杂度O(log2(n))
            // 如果t1没有指向任何内容,则会返回T2类型的初始值
            
            return 0;
        }
        
    • 一些C++的功能/特性

      • #include <bits/stdc++.h> // 标准库头文件
        using namespace std;
        int main() {
            
            __int128 a; // 128位整数,最大值大概10^38次方
           	// C++11以上可用,无法用标准方法读入
            
            cin.tie(0); cout.tie(0);
            ios::sync_with_stdio(false); 
            // 关闭cin、cout同步流,此举后不可混用scanf/printf
            
            auto x; // 自动变量,可以是任意属性
            // 举个例子
            std::set<int> st;
            std::for(auto i:st); // C++版for_each
            // 其中i是auto变量,也可改成set<int>::iterator
            
            // 所有的STL容器push/insert操作都可替换为emplcace
            // 速度上减小常数(不用临时变量)
            // 例:
            int i;
            std::set<int> st;
            st.emplace(i);
            std::vector<int> vc;
            vc.emplace_back(i);
            
            return 0;
        }
        
    • 强大的pb_ds库

      • #include <bits/stdc++.h>
        #include <bits/extc++.h> // 扩展库头文件
        /*
         * 这里如果没有bits/extc++.h的话需要
         * ext/pb_ds/tree_policy.hpp
         * ext/pb_ds/assoc_container.hpp
         * ext/pb_ds/priority_queue_policy.hpp
         * ext/pb_ds/trie_policy.hpp
         * ext/rope
         * ...
         */
        using namespace __gnu_pbds;
        using namespace __gnu_cxx;
        int main() {
        
            // 哈希表部分,用法与map一样,效率在C++11以下效率高
            // 注意,这部分在namespace __gnu_pbds下
            cc_hash_table<string, int> mp1; // 拉链法
            gp_hash_table<string, int> mp2; // 查探法(快一些)
        
            // 优先队列部分,比STL中高级
            priority_queue<int, std::greater<int>, TAG> pq;
            /*
             * 第一个参数是数据类型
             * 第二个是排序方式
             * 第三个是堆的类型
             * 其中堆的类型有下面几种
             * pairing_heap_tag
             * thin_heap_tag
             * binomial_heap_tag
             * c_binomial_heap_tag
             * binary_heap_tag
             * 其中pairing_heap_tag最快
             * 并且这个东西是带默认参数的,只需要定义一个int
             */
            // 比STL中的优先队列多了join和迭代器
            // 例子:
            priority_queue<int> pq1, pq2;
            pq1.join(pq2); // 会将pq2合并到pq1上
            pq1.begin(); pq1.end(); // 可遍历
        
            // 红黑树(平衡树)部分,与set相似,但更快
            tree <
                int,
                null_type,
                std::less<>,
                rb_tree_tag,
                tree_order_statistics_node_update
            > t, tre;
            /*
             * int 关键字类型
             * null_type 无映射(低版本g++为null_mapped_type)
             * less<int> 从小到大排序
             * rb_tree_tag 红黑树(splay_tree_tag splay)
             * tree_order_statistics_node_update 结点更新
             */
            int i, k;
            t.insert(i); // 插入
            t.erase(i); // 删除
            t.order_of_key(i);
            // 询问这个tree中有多少个比i小的元素
            t.find_by_order(k);
            // 找第k + 1小的元素的迭代器,如果order太大会返回end()
            t.join(tre); // tre合并到t上
            t.split(i, tre); // 小于等于i的保留,其余的属于tre
            // 基本操作有size()/empty()/begin()/end()等
            // 同样内置lower_bound/upper_bound
        
            // 可持久化平衡树部分
            // 注意,这部分在namespace __gun_cxx下
            rope<char> str;
            // 待我学习完后再更新
        
            return 0;
        }
        
  • 对拍

    • @echo off
      :loop
      随机数据生成.exe
      暴力.exe
      正解.exe
      fc 暴力.out 正解.out
      if not errorlevel 1 goto loop
      pause
      :end
      
      
  • 各种优化

    • #pragma GCC optimize(2)
      
      #define FAST ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);	//iostream特供
      
  • 快速读入输出(利用getchar)

    • 
      
  • 起 手 式

    • //不开longlong见祖宗 x年ACM一场空
      
      #define int long long
      ...
      
      signed main() {
      	...
      }
      

ACM模板整理

原文:https://www.cnblogs.com/Orzjh/p/14744546.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!