算法集
经历过夏令营的失败,但是我不会否认我做出过的所有努力。
——记在前面
基础
字符串
字符串里很有趣的东西
string res = a.substr(0,i + 1),rr = a.substr(0,i); //截取
reverse(rr.begin(),rr.end()); //反转
res+=rr; //连接
if (res.find(b) != res.npos)//字符串中匹配
哈希表
判断某个元素是否在集合中出现
unordered_set<char> hash; // 定义哈希表
for (auto c : s2) hash.insert(c); // 将s2中的字符插入哈希表
string res;
for (auto c : s1)
if (!hash.count(c)) //如果计数是0
res += c; //字符串可以直接+
字符串转数字:int a=atoi(x.c_str());
// int 转 string
stringstream ss;
int n = 123;
string str;
ss<<n;
ss>>str;
0x3f
string substr(int pos = 0,int n ) const;
函数说明:
参数1:pos是必填参数
参数2:n是可参数,表示取多少个字符,不填表示截取到末尾
该函数功能为:返回从pos开始的n个字符组成的字符串,原字符串不被改变
精度
进制转换
int j2dec(int j, int num)
{
int res = 0;
int i = 1;
while(num)
{
res = res + i * (num % 10);
num = num / 10;
i = i * j;
}
return res;
}
int dec2k(int k, int num)
{
int res = 0;
int i = 1;
while(num)
{
res = res + i * (num % k);
num = num / k;
i = i * 10;
}
return res;
}
前缀和与差分
for(int i=1;i<=n;i++) {S[i]=S[i-1]+q[i];} //求前缀和
差分:由前缀和求原数组
void insert(int l, int r,int x) //原数组
{
p[l]+=x;
p[r+1]-=x;
}
for(int i=1;i<=m;i++)
S[i]=S[i-1]+p[i]; //创造前缀和,这里指的是区间内容
二分
找到起始坐标:1 2 2 3 3 4 使用lower_bound() 和 upper_bound
int l = lower_bound(q,q+n,x)-q; //如果找不到才返回第一个比他大的
if(q[l]!=x)
cout<<"-1 -1"<<endl;
else
{
cout<<l<<" ";
int r = upper_bound(q,q+n,x)-q; //返回第一个比他大的,无等于
cout<<r-1<<endl;
}
位运算
求a中1的个数
i的第k位是否为1
i >> k & 1 //右移k位
int lowbit(int x)//求最后一位1的下标
{
return x & -x;
}
int num=0;
while(a)
{
int y=lowbit(a);
a-=y;
num++;
}
cout<<num<<" ";
数学
质数
判定:
试除法:
i
<
=
n
/
i
i<=n/i
i<=n/i
埃氏筛
bool heshu[maxn];
void sieve()
{
for(int i=2; i<=maxn; i++) //从2开始往后筛
{
if(!heshu[i])
{
for(int j=2*i; j<=maxn; j+=i)
{
heshu[j]=true;
}
}
}
}
约数
N
=
P
1
α
1
P
2
α
2
P
3
α
3
P
4
α
4
.
.
.
P
k
α
k
N=P_1^{\alpha_1}P_2^{\alpha_2}P_3^{\alpha_3}P_4^{\alpha_4}...P_k^{\alpha_k}
N=P1α1P2α2P3α3P4α4...Pkαk 这些P是质因数
约束个数:
(
α
1
+
1
)
(
α
2
+
1
)
(
α
3
+
1
)
.
.
.
(
α
k
+
1
)
(\alpha_1+1)(\alpha_2+1)(\alpha_3+1)...(\alpha_k+1)
(α1+1)(α2+1)(α3+1)...(αk+1),最多1540个
unordered_map<int, int> primes; //哈希表
int x;
cin >> x;
for (int i = 2; i <= x / i; i ++ )
while (x % i == 0)
{
x /= i;
primes[i] ++ ; //存的是{i, 个数}
}
if (x > 1) primes[x] ++ ;
LL res = 1;
for (auto p : primes) res = res * (p.second + 1) % mod;
cout << res << endl;
约束之和: ( P 1 0 + P 1 1 . . . + P 1 k ) . . . ( P k 0 + P k 1 . . . + P k k ) (P_1^0+P_1^1...+P_1^k)...(P_k^0+P_k^1...+P_k^k) (P10+P11...+P1k)...(Pk0+Pk1...+Pkk)
unordered_map<int, int> primes;
int x;
cin >> x;
for (int i = 2; i <= x / i; i ++ )
while (x % i == 0)
{
x /= i;
primes[i] ++ ;
}
if (x > 1) primes[x] ++ ;
LL res = 1;
for (auto p : primes)/主要是这里
{
LL a = p.first, b = p.second;
LL t = 1;
while (b -- ) t = (t * a + 1) % mod;
res = res * t % mod;
}
cout << res << endl;
}
最大公约数:__gcd(a,b)
最小公倍数:a * (b/__gcd(a, b));
容斥定理
首先要知道每个集合怎么表示
S
1
∪
S
2
∪
S
3
=
∣
S
1
∣
+
∣
S
2
∣
+
∣
S
3
∣
−
∣
S
1
∩
S
2
∣
.
.
.
S_1\cup S_2\cup S_3=|S_1|+|S_2|+|S_3|-|S_1\cap S_2|...
S1∪S2∪S3=∣S1∣+∣S2∣+∣S3∣−∣S1∩S2∣...
∣
S
1
∩
S
2
∣
=
∣
n
p
1
p
2
∣
|S_1\cap S_2|=| \frac n {p_1p_2}|
∣S1∩S2∣=∣p1p2n∣
S
1
:
1
—
n
中
p
1
的
倍
数
的
个
数
S_1:1—n中p1的倍数的个数
S1:1—n中p1的倍数的个数
所以集合:位运算,有
2
n
−
1
2^n-1
2n−1个集合,枚举所有选法(1~
2
n
−
1
2^n-1
2n−1)
C
n
0
+
.
.
.
C
n
n
=
2
n
C_n^0+...C_n^n=2^n
Cn0+...Cnn=2n
for(i=1; i<2^n;i++) 00101 1表示选这个集合
给定一个整数
n
n
n 和
m
m
m 个不同的质数,求出
1
∼
n
1∼n
1∼n 中能被
p
1
,
p
2
,
…
,
p
m
p_1,p_2,…,p_m
p1,p2,…,pm 中的至少一个数整除的整数有多少个
int res = 0;
for (int i = 1; i < 1 << m; i ++ ) //i<2^m,每种选法
{
int t = 1, s = 0; //t:当前质数的乘积,s:几个集合
for (int j = 0; j < m; j ++ )
if (i >> j & 1) //
{
s++;
if ((LL)t * p[j] > n) //超过n,不算了
{
t = -1;
break;
}
t *= p[j];
}
if (t != -1)//奇数+,偶数-
{
if (s % 2) res += n / t;
else res -= n / t;
}
}
cout << res << endl;
快速幂
O(log k)
求
a
k
m
o
d
p
a^k mod p
akmodp
把k拆掉——二进制,求 a 模 p 的乘法逆元:qmi(a, p - 2, p)
LL qmi(int a, int b, int p)
{
LL res = 1 % p;
while (b)
{
if (b & 1) res = res * a % p;
a = a * (LL)a % p;
b >>= 1;
}
return res;
}
printf("%lld\n", qmi(a, b, p));
//求逆元:qmi(a, p - 2, p)求逆元
图论
DFS | BFS |
---|---|
不具最短性 | 最短路 |
stack | queue |
空间复杂度:O(h) | 空间复杂度:O(2^h) |
图存储
vector <int> map[max] ; //无权
bool st[N];
struct Edge{
int to;
int cost;
};
vector <Edge> G[max]; //有权
//输入
for(int i=1; i<n; ++i){
int u, v;
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
图的dfs
int dfs(int u) //以u为根的子树点数量
{
st[u]=true; //搜过了
for(auto v:G[u])
{
if(!st[v])
x=dfs(v);
}
}
图的bfs
vector<int> G[N];
int path[N];
queue <int> p;
int bfs()
{
memset(path,-1,sizeof(path));
path[1]=0;
p.push(1);
while(p.size())
{
auto h = p.front();
p.pop();
for (auto v:G[h])
{
if(path[v]==-1)
{path[v]=path[h]+1; p.push(v);}
}
}
return path[n];
}
cout<<bfs();
求拓扑序列
queue<int> p; //队列次序就是拓扑序
vector<int> G[N];
vector <int> pre; //记录队列
int d[N];//入度
bool topsort()
{
for(int i=1;i<=n;i++)
if (!d[i]) //入度=0.进队列
p.push(i);
while(p.size())
{
int h=p.front();
p.pop();
pre.push_back(h); //记录队列
for(auto v:G[h])
{
d[v]--;
if (d[v]==0)
p.push(v);
}
}
int len=pre.size();
return len==n; //是否有拓扑排序
}
if(topsort())
for(auto i:pre)
cout<<i<<" ";
矩阵的dfs
矩阵:求排列
const int N = 10;
int path[N]; //存储过程
bool b[N]; //点遍历标记
int n;
void dfs(int l) //l是层数
{
if (l==n) //到最后一层了
{
for(int i = 0 ;i <n; i++) cout<<path[i]<<" ";
cout<<endl;
return;
}
for(int i = 1; i <= n; i++ )
{
if(b[i]==false)
{
b[i]=true;
path[l]=i;
dfs(l+1);
b[i]=false; //回溯
}
}
}
dfs(0);
n皇后
void dfs (int u)
{
if (u==n)
{
for(int i = 0; i<n; i++)
{
for(int j = 0; j<n; j++)
cout<<map[i][j];
cout<<endl;
}
return;
}
for(int i = 0; i<n; i++)
{
if(!col[i] && !ldjx[u-i+n] && !rdjx[u+i])
{
map[i][u]='Q';
col[i]=ldjx[u-i+n]=rdjx[u+i]=true;
dfs(u+1);
col[i]=ldjx[u-i+n]=rdjx[u+i]=false;
map[i][u]='.';
}
}
}
矩阵的bfs
走迷宫最少移动次数
typedef pair<int,int> PII;
queue <PII> p;
int mm[N][N]; //迷宫
int path[N][N]; //走过否
PII Prev[N][N]; //记录路径
int bfs(int n,int m)
{
int dx[4]= {1,-1,0,0};
int dy[4]= {0,0,-1,1};
while(p.size())
{
auto t=p.front();
p.pop();
for(int i=0; i<4; i++)
{
int x=t.first+dx[i];
int y=t.second+dy[i];
if(x<n&&x>=0&&y<m&&y>=0&&mm[x][y]==0&&path[x][y]==-1)
{
path[x][y]=path[t.first][t.second]+1;
Prev[x][y]=t;
p.push({x,y});
}
}
//记录路径
/*
int x=n-1,y=m-1;
while(x||y)
{
cout<<x<<’ ’<<y;
auto t=Prev[x][y];
x=t.first,y=t.second;
}*/
return path[n-1][m-1];
}
}
p.push({0,0});
memset(path,-1,sizeof(path));
path[0][0]=0;
cout<<bfs(n,m)<<endl; //(n,m)是右下角
最短路
Dijskstra
求出 1 号点到 n 号点的最短距离
朴素Dijskstra S:当前已确定最短距离的点 (O(N^2))
- 初始化距离(起点)
- for v 0:n
t:不在s中的距离最近的点
t入s
更新其他点距离(m次)
int g[N][N]; //为稠密阵所以用邻接矩阵存储
int dist[N]; //用于记录每一个点距离第一个点的距离
bool st[N]; //用于记录该点的最短距离是否已经确定
int n,m;
int Dijkstra()
{
memset(dist, 0x3f,sizeof dist); //初始化距离 0x3f代表无限大
dist[1]=0; //第一个点到自身的距离为0
for(int i=0;i<n;i++) //有n个点所以要进行n次 迭代
{
int t=-1; //t存储当前访问的点
for(int j=1;j<=n;j++) //这里的j代表的是从1号点开始
if(!st[j]&&(t==-1||dist[t]>dist[j]))
t=j;
st[t]=true;
for(int j=1;j<=n;j++) //依次更新每个点所到相邻的点路径值
dist[j]=min(dist[j],dist[t]+g[t][j]);
}
if(dist[n]==0x3f3f3f3f) return -1; //如果第n个点路径为无穷大即不存在最低路径
return dist[n];
}
memset(g,0x3f,sizeof g); //初始化图 因为是求最短路径
while(m--)
{
int x,y,z;
cin>>x>>y>>z;
g[x][y]=min(g[x][y],z); //如果发生重边的情况则保留最短的一条边
}
cout<<Dijkstra()<<endl;
堆优化
struct edge{int to; int cost;};
typedef pair<int,int> PII;
vector<edge> G[N];
bool st[N];
int dist[N];
int n,m;
int Dijkstra()
{
memset(dist, 0x3f,sizeof dist);
dist[1]=0;
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0,1});
while(heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.second, distance=t.first;
if(st[ver]) continue; //已经处理过了
st[ver]=true;
for (auto v:G[ver])
{
if(dist[v.to]>distance+v.cost)
{
dist[v.to]=distance+v.cost;
heap.push({dist[v.to],v.to});
}
}
}
if(dist[n]==0x3f3f3f3f) return -1; //如果第n个点路径为无穷大即不存在最低路径
return dist[n];
}
for(int i=0;i<m;i++)
{
edge a;
int j, s, t;
cin >>j>> s >> t;
a.to = s;
a.cost = t;
G[j].push_back(a);//添加
}
cout<<Dijkstra();
Bellman-Ford
处理负权边下的最短路:从1号点不多余k条边到k点的距离,没有负权回路
遍历n次
for 所有边
更新:dist[b] = min(dist[b], dist[a] + w) O(nm)
struct Edge
{
int a, b, c;
}edges[M];
int dist[N];
int last[N];
void bellman_ford()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for (int i = 0; i < k; i ++ ) //最多k边
{
memcpy(last, dist, sizeof dist); //备份,防止串联
for (int j = 0; j < m; j ++ ) //遍历边
{
auto e = edges[j];
dist[e.b] = min(dist[e.b], last[e.a] + e.c);
}
}
}
for (int i = 0; i < m; i ++ )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
edges[i] = {a, b, c};
}
bellman_ford();
if (dist[n] > 0x3f3f3f3f / 2) puts("impossible");
else printf("%d\n", dist[n]);
Floyd
基于动态规划,求多源汇最短路O(n^3)
k:1~n
i:1~n
j:1~n
d[i , j] = min(d[i , j], d[i , k] + d[k, j]) d[i,j] i到j的最短路
const int N = 210, INF = 1e9;
int n, m, Q;
int d[N][N];
void floyd()
{
for (int k = 1; k <= n; k ++ )
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
if (i == j) d[i][j] = 0;
else d[i][j] = INF; //初始化邻接矩阵
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
d[a][b] = min(d[a][b], c); //重边
}
floyd();
int a, b;
scanf("%d%d", &a, &b);
int t = d[a][b];
if (t > INF / 2) puts("impossible");
else printf("%d\n", t);
dij+dfs
#include <cstdio>
#include <iostream>
#include <vector>
#include <cstring>
#include <cmath>
using namespace std;
#define INF 999999
#define MAX 505
int cmax, n, sp, m;
int v[MAX];
int mp[MAX][MAX];
int dist[MAX];
int minsend = INF, minback = INF;
bool visited[MAX] = {false};
vector<int> prenode[MAX];
vector<int> path, tmppath;
void dijkstra(int s){
fill(dist, dist + MAX, INF);
dist[s] = 0;
while(true){
int u=-1, mint = INF;
for(int i=0; i<=n; i++){
if(!visited[i] && dist[i] < mint){
mint = dist[i];
u = i;
}
}
if(u == -1)
return;
visited[u] = true;
for(int i=0; i<=n; i++){
if(!visited[i] && mp[u][i] != INF){
if(dist[u] + mp[u][i] < dist[i]){
dist[i] = dist[u] + mp[u][i];
prenode[i].clear();
prenode[i].push_back(u);
}
else if(dist[u] + mp[u][i] == dist[i]){
prenode[i].push_back(u);
}
}
}
}
}
void dfs(int vi){
if(vi == 0){
tmppath.push_back(0);
int minsendtmp = 0, minbacktmp = 0;
for(int i=tmppath.size()-2; i>=0; i--){
int t = tmppath[i];
if(minbacktmp + v[t] < cmax/2)
minsendtmp += cmax/2 - (minbacktmp + v[t]), minbacktmp = 0;
else{
if(v[t] > cmax/2)
minbacktmp += v[t] - cmax/2;
else
minbacktmp -= cmax/2 - v[t];
}
}
// 测试输出,提交时要注释掉
// printf("%d ", minsendtmp);
// for(int i=tmppath.size()-1; i>=0; i--){
// printf("%d", tmppath[i]);
// if(i)
// printf("->");
// }
// printf(" %d\n", minbacktmp);
if(minsendtmp < minsend)
path = tmppath, minsend = minsendtmp, minback = minbacktmp;
else if(minsendtmp == minsend && minbacktmp < minback)
path = tmppath, minback = minbacktmp;
tmppath.pop_back();
return;
}
tmppath.push_back(vi);
for(int i=0; i<prenode[vi].size(); i++)
dfs(prenode[vi][i]);
tmppath.pop_back();
}
int main(){
fill(mp[0], mp[0] + MAX*MAX, INF);
scanf("%d %d %d %d", &cmax, &n, &sp, &m);
for(int i=1; i<=n; i++)
scanf("%d", v+i);
for(int i=0; i<m; i++){
int x, y, z;
scanf("%d %d %d", &x, &y, &z);
mp[x][y] = z;
mp[y][x] = z;
}
dijkstra(0);
dfs(sp);
printf("%d ", minsend);
for(int i=path.size()-1; i>=0; i--){
printf("%d", path[i]);
if(i)
printf("->");
}
printf(" %d\n", minback);
return 0;
}
最小生成树
无向图,稠密用朴素prim,稀疏用kruskal
铺公路的最短距离
Prim
- 初始化距离正无穷;
- 迭代,找到不在集合中的最小距离点,更新其他点到集合的距离(就是该集合的最短边)
const int N = 510, INF = 0x3f3f3f3f;
int n, m;
int g[N][N];
int dist[N];
bool st[N];
int prim()
{
memset(dist, 0x3f, sizeof dist);
int res = 0;
for (int i = 0; i < n; i ++ )
{
int t = -1;
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
if (i && dist[t] == INF) return INF; //不连通
if (i) res += dist[t];
st[t] = true;
for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
}
return res;
}
scanf("%d%d", &n, &m);
memset(g, 0x3f, sizeof g);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
g[a][b] = g[b][a] = min(g[a][b], c);
}
int t = prim();
if (t == INF) puts("impossible"); //所有点不连通
else printf("%d\n", t);
Kruskal
- 排序所有边:大——小 O(mlogm)
- 枚举每条边 a b c,若a与b不连通,将该边加入集合中 (并查集)
const int N = 100010, M = 200010, INF = 0x3f3f3f3f;
int n, m;
int p[N];
struct Edge
{
int a, b, w;
bool operator< (const Edge &W)const
{
return w < W.w;
}
}edges[M];
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int kruskal()
{
sort(edges, edges + m); //排序
for (int i = 1; i <= n; i ++ ) p[i] = i; // 初始化并查集
int res = 0, cnt = 0;
for (int i = 0; i < m; i ++ )
{
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
a = find(a), b = find(b);
if (a != b)
{
p[a] = b;
res += w;
cnt ++ ;
}
}
if (cnt < n - 1) return INF;
return res;
}
scanf("%d%d", &n, &m);
for (int i = 0; i < m; i ++ )
{
int a, b, w;
scanf("%d%d%d", &a, &b, &w);
edges[i] = {a, b, w};
}
int t = kruskal();
if (t == INF) puts("impossible");
else printf("%d\n", t);
二分图
用染色法判断是不是二分图,二分图=图中无奇数环(变为奇数)dfs
遍历点,只要一个点确定了,该连通块的确定了
int n, m;
vector<int> G[N];
int color[N];
bool dfs(int u, int c)
{
color[u] = c;
for(auto v:G[u])
{
if (!color[v])
if (!dfs(v, 3 - c)) return false;
else if (color[v] == c) return false;
}
return true;
}
cin>>n>>m;
while (m -- )
{
int a, b;
cin>>a>>b;
G[a].push_back(b);
G[b].push_back(a);
}
bool flag = true;
for (int i = 1; i <= n; i ++ )
if (!color[i])
{
if (!dfs(i, 1))
{
flag = false;
break;
}
}
if (flag) puts("Yes");
else puts("No");
建树遍历
int num[50];//每一层个数
int n, a[maxn];
int p[50], lson[maxn], rson[maxn], cur[maxn];
int dep[maxn], id = 1;
int root, idx=1;
void build(int & rt, int d){
rt = idx ++;
if(p[d+1] < num[d+1]){
build(lson[rt], d+1);
}
cur[rt] = a[id ++];
p[d] += 1;
if(p[d+1] < num[d+1]){
build(rson[rt], d+1);
}
}
sort(a+1, a+1+n);
memset(num, 0, sizeof(num));
num[0] =1;
int sum = 1;
for(int i=1; i<30 ; ++i){
num[i] = num[i-1] * 2;
if(sum + num[i] <= n){
sum += num[i];
}else{
num[i] = n - sum;
break;
}
}
build(root, 0);
动态规划
树形dp
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 300010, M = N * 2;
int n;
int v[N];
int h[N], e[M], w[M], ne[M], idx;
LL ans;
void add(int a, int b, int c) // 添加一条边a->b,边权为c
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
LL dfs(int u, int father)
{
LL d1 = 0, d2 = 0;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (j == father) continue;
LL d = dfs(j, u);
if (d < w[i]) continue;
d -= w[i];
if (d >= d1) d2 = d1, d1 = d;
else if (d > d2) d2 = d;
}
ans = max(ans, d1 + d2 + v[u]);
return d1 + v[u];
}
int main()
{
scanf("%d", &n);
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i ++ ) scanf("%d", &v[i]);
for (int i = 0; i < n - 1; i ++ )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c), add(b, a, c);
}
ans = max(ans, dfs(1, -1));
printf("%lld\n", ans);
return 0;
}
数据结构
栈
表达式求解模板
#include<bits/stdc++.h> //
using namespace std;
stack<int> num;
stack<char> op;
void eval()
{
auto b = num.top(); num.pop();
auto a = num.top(); num.pop();
auto c = op.top(); op.pop();
int x;
if (c == '+') x = a + b;
else if (c == '-') x = a - b;
else if (c == '*') x = a * b;
else x = a / b;
num.push(x);
}
int main()
{
unordered_map<char, int> pr{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}}; //定义优先级
string str;
cin >> str;
for (int i = 0; i < str.size(); i ++ )
{
auto c = str[i];
if (isdigit(c)) //是数字,则抠出来,入num
{
int x = 0, j = i;
while (j < str.size() && isdigit(str[j]))
x = x * 10 + str[j ++ ] - '0';
i = j - 1;
num.push(x);
}
else if (c == '(') op.push(c); //左括号,直接入
else if (c == ')') // 右括号,做运算
{
while (op.top() != '(') eval();
op.pop();//弹出左括号
}
else
{
while (op.size() && op.top() != '(' && pr[op.top()] >= pr[c]) //栈顶>当前:eval栈顶元素
eval();
op.push(c); //否则插入
}
}
while (op.size())
eval();
cout << num.top() << endl;
return 0;
}
单调栈
求左边第一个比他小的数
记录单调上升的一个栈
for(int i=1;i<=n;i++)
{
cin>>a[i];
while(s.size()>0&&s.top()>=a[i])
{
int h=s.top();
if(h>=a[i])
s.pop();
}
if(!s.size()) cout<<-1<<" ";
else
cout<<s.top()<<" ";
s.push(a[i]);
}
单调队列
并查集
快速处理: 近乎O(1)
- 合并两个集合
- 询问两个元素是否在一个集合
优化:第一次求之后,把所有点的父亲都变成根(路径压缩)
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N = 1e5+10;
int p[N];
int find(int x) // 返回祖宗节点+路径压缩
{
if(p[x]!=x)
p[x]=find(p[x]);
return p[x];
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
p[i]=i; //初始化
while(m--)
{
char op;
int a,b;
cin>>op>>a>>b;
if(op=='M')
p[find(a)]=find(b);
else
{
if(find(a)==find(b))
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
}
return 0;
}
连通块中的点
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N = 1e5+10;
int p[N];
int siz[N];
int find(int x) // 返回祖宗节点+路径压缩
{
if(p[x]!=x)
p[x]=find(p[x]);
return p[x];
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
p[i]=i; //初始化
siz[i]=1;
}
while(m--)
{
char op[5];
int a,b;
cin>>op;
if(op[0]=='C')
{
cin>>a>>b;
if(find(a)==find(b)) continue;
siz[find(b)]+=siz[find(a)];
p[find(a)]=find(b);
}
else if(op[1]=='1')
{
cin>>a>>b;
if(find(a)==find(b))
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
else
{
cin>>a;
cout<<siz[find(a)]<<endl;
}
}
return 0;
}
贪心
排序
枚举
const int N = 100010;
int n;
struct Range
{
int l, r;
bool operator< (const Range &W)const
{
return r < W.r;
}
}range[N];
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) scanf("%d%d", &range[i].l, &range[i].r);
sort(range, range + n);
int res = 0, ed = -2e9;
for (int i = 0; i < n; i ++ )
if (range[i].l > ed)
{
res ++ ;
ed = range[i].r;
}
printf("%d\n", res);