算法集 just do something

经历过夏令营的失败,但是我不会否认我做出过的所有努力。
——记在前面

基础

字符串

字符串里很有趣的东西

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|... S1S2S3=S1+S2+S3S1S2...
∣ S 1 ∩ S 2 ∣ = ∣ n p 1 p 2 ∣ |S_1\cap S_2|=| \frac n {p_1p_2}| S1S2=p1p2n S 1 : 1 — n 中 p 1 的 倍 数 的 个 数 S_1:1—n中p1的倍数的个数 S1:1np1
所以集合:位运算,有 2 n − 1 2^n-1 2n1个集合,枚举所有选法(1~ 2 n − 1 2^n-1 2n1
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 1n 中能被 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)求逆元

图论

DFSBFS
不具最短性最短路
stackqueue
空间复杂度: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))

  1. 初始化距离(起点)
  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

  1. 初始化距离正无穷;
  2. 迭代,找到不在集合中的最小距离点,更新其他点到集合的距离(就是该集合的最短边)
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

  1. 排序所有边:大——小 O(mlogm)
  2. 枚举每条边 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)

  1. 合并两个集合
  2. 询问两个元素是否在一个集合
    在这里插入图片描述
    优化:第一次求之后,把所有点的父亲都变成根(路径压缩
#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);

性质与证明

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值