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;
}
//通用模板
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;
}
#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;
}
/*
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;
}
/*
可采用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
*/
#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;
}
#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\)的值。
#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;
}
#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;
}
#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;
#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;
}
#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;
}
#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特供
//不开longlong见祖宗 x年ACM一场空
#define int long long
...
signed main() {
...
}
原文:https://www.cnblogs.com/Orzjh/p/14744546.html