基本算法的一些模板

基本算法

数据范围反推算法内容(c++)

数据范围反推算法内容

# << 左移
二进制左移,填充0
3 << 2  === 3 * 2 * 2

# >> 右移
正数填充0,负数填充1,因此,右移负数始终为负数,正数始终为正数
27 >> 3 === 24 / 3 / 3

# >>> 无符号右移
正数右移补0, 负数也补0,因此负数会转为正数,负数会达到int最大值
-1 >>> 1 === 2147483647
16 >>> 2 === 16/2/2

# & 与
都为1时为1,其余情况为0

# | 或
有一个1则值为1,全为0值为0

# ^ 异或
相同为0, 不同为1

# ~
0变1,1变0

# 原码,反码,补码
	• 第一位0,1决定正负,其余位是数字的大小,绝对值相同的数字除第一位外的其余位是相同
	• 正数:原码、反码、补码全部相同
	• 负数:
		○ 反码:除第一位外,其余位取反
	补码:反码+1

二分

int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    return l;
}
int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}
double bsearch_3(double l, double r)
{
    const double eps = 1e-6;
    while (r - l > eps)
    {
        double mid = (l + r) / 2;
        if (check(mid)) r = mid;
        else l = mid;
    }
    return l;
}

高精

高精度加法

#include <iostream>
#include <vector>

using namespace std;

vector<int> add(vector<int> a, vector<int> b) {
    vector<int> c;
    int la = a.size(), lb = b.size();
    if (la < lb) return add(b, a);

    int t = 0;
    for (int i = 0; i < la; ++i) {
        t += a[i];
        if (i < lb) t += b[i];
        c.push_back(t % 10);
        t /= 10;
    }

    if (t) c.push_back(t);
    return c;

}

int main(void) {
    vector<int> a, b;
    string A, B;
    cin >> A >> B;

    for (int i = A.size() - 1; i >= 0; --i) a.push_back(A[i] - '0');
    for (int i = B.size() - 1; i >= 0; --i) b.push_back(B[i] - '0');

    vector<int> c = add(a, b);

    for (int i = c.size() - 1; i >= 0; --i) cout << c[i];

    return 0;

}

高精度减法

#include <iostream>
#include <vector>

using namespace std;

bool cmp(vector<int> a, vector<int> b) {
    if (a.size() != b.size()) return a.size() > b.size();

    for (int i = a.size() - 1; i >= 0; --i)
        if (a[i] != b[i]) return a[i] > b[i];

    return true;
}

vector<int> sub(vector<int> a, vector<int> b) {
    vector<int> c;
    int t = 0;
    for (int i = 0; i < a.size(); ++i) {
        t = a[i] - t;
        if (i < b.size()) t -= b[i];
        c.push_back((t + 10) % 10);
        if (t < 0) t = 1;
        else t = 0;
    }

    while (c.size() > 1 && c.back() == 0) c.pop_back();
    return c;
}

int main(void) {
    string A, B;
    cin >> A >> B;

    vector<int> a, b, c;
    for (int i = A.size() - 1; i >= 0; --i) a.push_back(A[i] - '0');
    for (int i = B.size() - 1; i >= 0; --i) b.push_back(B[i] - '0');

    if (cmp(a, b)) c = sub(a, b);
    else c = sub(b, a), cout << '-';

    for (int i = c.size() - 1; i >= 0; --i) cout << c[i];
    return 0;

}

高精度乘法

#include <iostream>
#include <vector>

using namespace std;

vector<int> mul(vector<int> a, int b) {
    vector<int> c;
    int t = 0;
    for (int i = 0; i < a.size() || t; ++i) {
        if (i < a.size()) t += a[i] * b;
        c.push_back(t % 10);
        t /= 10;
    }
    while (c.size() > 1 && c.back() == 0) c.pop_back();

    return c;
}

int main(void) {
    string A;
    int b;
    cin >> A >> b;

    vector<int> a;
    for (int i = A.size() - 1; i >= 0; --i) a.push_back(A[i] - '0');

    vector<int> c = mul(a, b);
    for (int i = c.size() - 1; i >= 0; --i) cout << c[i];

    return 0;
}

高精度除法和取余

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

vector<int> div(vector<int> a, int b, int &r) {
    vector<int> c;
    r = 0;
    for (int i = a.size() - 1; i >= 0; --i) {
        r = r * 10 + a[i];
        c.push_back(r / b);
        r %= b;
    }

    reverse(c.begin(), c.end());
    while (c.size() > 1 && c.back() == 0) c.pop_back();

    return c;
}

int main(void) {
    string A;
    int b;
    vector<int> a, c;
    cin >> A >> b;
    for (int i = A.size() - 1; i >= 0; --i) a.push_back(A[i] - '0');

    int r;
    c = div(a, b, r);
    for (int i = c.size() - 1; i >= 0; --i) cout << c[i];

    cout << endl << r;
    return 0;

}

双指针

最长连续不重复子序列

/*给定一个长度为 n 的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。*/
#include <iostream>

using namespace std;

const int N = 100010;

int a[N], st[N], n;

int main(void) {
    cin >> n;
    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);

    int res = 0;
    for (int i = 1, j = 1; i <= n; ++i) {
        st[a[i]]++;
        while (j < i && st[a[i]] > 1) st[a[j++]]--;
        res = max(res, i - j + 1);
    }

    cout << res;
    return 0;
}

数组元素的目标和

/* 数组a,b递增,求ai+bj==x时i,j下标,注意:模板规定下标从0开始 */

#include <iostream>

using namespace std;

const int N = 100010;

int a[N], b[N];
int n, m, x;

int main(void) {
    scanf("%d%d%d", &n, &m, &x);
    for (int i = 1; i <= n; ++i) scanf("%d", a + i);
    for (int i = 1; i <= m; ++i) scanf("%d", b + i);

    for (int i = 1, j = m; i <= n; ++i) {
        while (j >= 1 && a[i] + b[j] > x) j--;
        if (a[i] + b[j] == x) {
            printf("%d %d", i - 1, j - 1);
            break;
        }
    }
    return 0;
}

判断子序列

数组a是否为数组b的子序列(指按原有次序排列

#include <iostream>

using namespace std;

const int N = 100010;

int a[N], b[N], n, m;

int main(void) {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i) scanf("%d", a + i);
    for (int i = 1; i <= m; ++i) scanf("%d", b + i);

    int i = 1;
    for (int j = 1; j <= m; ++j) {
        if (a[i] == b[j]) i++;
    }

    if (i == n + 1) puts("Yes");
    else puts("No");

    return 0;
}

离散化

求区间和(保序)

记得开多倍数组

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

typedef pair<int, int> PII;

const int N = 100010;

vector<PII> add, query;
vector<int> all;
int a[N * 3], s[N * 3];
int n, q;

int find(int x) {
    int l = 0, r = all.size() - 1;
    while (l < r) {
        int mid = l + r + 1 >> 1;
        if (all[mid] < x) l = mid;
        else r = mid - 1;
    }
    return r + 1;
}

int main(void) {
    scanf("%d%d", &n, &q);

    for (int i = 1; i <= n; ++i) {
        int x, v;
        scanf("%d%d", &x, &v);
        add.push_back({x, v});
        all.push_back(x);
    }

    for (int i = 1; i <= q; ++i) {
        int l, r;
        scanf("%d%d", &l, &r);
        query.push_back({l, r});
        all.push_back(l);
        all.push_back(r);
    }

    sort(all.begin(), all.end());
    all.erase(unique(all.begin(), all.end()), all.end());

    for (int i = 0; i < add.size(); ++i) {
        int x = add[i].first, v = add[i].second;
        x = find(x);
        a[x] += v;
    }

    for (int i = 1; i <= all.size(); ++i) s[i] = s[i - 1] + a[i];

    for (int i = 0; i < query.size(); ++i) {
        int l = query[i].first, r = query[i].second;
        l = find(l), r = find(r);
        printf("%d\n", s[r] - s[l - 1]);
    }

    return 0;
}

其他

区间合并

给定 n个区间[l, r],要求合并所有有交集的区间。

#include <iostream>
#include <vector>
#include <algorithm>

#define l first
#define r second

using namespace std;

typedef pair<int, int> PII;
vector<PII> segs;
int n;

int merge() {
    vector<PII> res;
    int l = -2e9, r = -2e9;
    for (int i = 0; i < segs.size(); ++i) {
        PII t = segs[i];
        if (r < t.l) {
            if (r != -2e9) res.push_back({l, r});
            l = t.l, r = t.r;
        }
        else
            r = max(r, t.r);
    }
    if (l != -2e9) res.push_back({l, r});
    
    return res.size();
}

int main(void) {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        int l, r;
        scanf("%d%d", &l, &r);
        segs.push_back({l, r});
    }
    
    sort(segs.begin(), segs.end());
    
    cout << merge();
    return 0;
}

动态中位数

依次读入一个整数序列,每当已经读入的整数个数为奇数时,输出已读入的整数构成的序列的中位数。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

int main()
{
    int T;
    scanf("%d", &T);
    while (T -- )
    {
        int n, m;
        scanf("%d%d", &m, &n);
        printf("%d %d\n", m, (n + 1) / 2);

        priority_queue<int> down;
        priority_queue<int, vector<int>, greater<int>> up;


        int cnt = 0;
        for (int i = 1; i <= n; i ++ )
        {
            int x;
            scanf("%d", &x);

            if (down.size() == 0 || x <= down.top()) down.push(x);
            else up.push(x);

            if (down.size() > up.size() + 1) up.push(down.top()), down.pop();
            if (up.size() > down.size()) down.push(up.top()), up.pop();


            if (i % 2)
            {
                printf("%d ", down.top());
                if ( ++ cnt % 10 == 0) puts("");
            }
        }

        if (cnt % 10) puts("");
    }

    return 0;
}

增减序列

给定一个长度为 n 的数列 a1,a2,…,an,每次可以选择一个区间 [l,r],使下标在这个区间内的数都加一或者都减一。

求至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列可能有多少种。
    
第一行输出最少操作次数。

第二行输出最终能得到多少种结果。
#include <iostream>

using namespace std;

typedef long long LL;

const int N = 100010;

int a[N], d[N];
int n;

int main(void) {
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    for (int i = 1; i <= n; ++i) d[i] = a[i] - a[i - 1];

    LL p = 0, q = 0;
    for (int i = 2; i <= n; ++i)
        if (d[i] > 0) p += d[i];
        else q -= d[i];

    cout << max(p, q) << endl;
    cout << (abs(p - q) + 1) ;

    return 0;
}
posted @ 2021-07-10 14:21  yangruomao  阅读(64)  评论(0编辑  收藏  举报