基础算法进阶

lowbit

非负整数n在二进制表示的形式,最低为的1和以及最低位1后的所有0组成的数
lowbit(n) = n & (-n)

快速幂

typedef long long LL;

int qmi(int a, int k, int p)  // 求a^k mod p
{
    //这一步不能少
    a %= p;
    int res = 1 % p;
    while (k)
    {
        if (k & 1) res = (LL)res * a % p;
        a = (LL)a * a % p;
        k >>= 1;
    }
    return res;
}

约数之和(acwing 97)

题意: 求 A B A^B AB的所有约数之和
分析: A 可 以 分 解 质 因 数 变 成 a 1 b 1 ∗ a 2 b 2 ∗ . . . ∗ a n b n A可以分解质因数变成a_{1}^{b1} * a_{2}^{b2} * ...* a_{n}^{bn} Aa1b1a2b2...anbn,
则 A B 分 解 质 因 数 可 以 变 成 a 1 B ∗ b 1 ∗ a 2 B ∗ b 2 ∗ . . . ∗ a n B ∗ b n 则A^B分解质因数可以变成a_{1}^{B * b1}* a_{2}^{B * b2}*...*a_{n}^{B * bn} ABa1Bb1a2Bb2...anBbn 因 为 a 1 , . . . , a n 是 A B 的 约 数 , 所 以 a 1 b 1 ∗ . . . ∗ a n b n 也 是 A B 的 约 数 因为a_{1},...,a_{n}是A^B的约数,所以a_{1}^{b1}*...*a_{n}^{bn}也是A^B的约数 a1,...,anABa1b1...anbnAB
a 1 b 1 的 取 法 有 b 1 + 1 种 , 例 如 a 1 0 , a 1 1 , . . . , a 1 b 1 a_1^{b1}的取法有b1 + 1种,例如a_1^0,a_1^1,...,a_1^{b1} a1b1b1+1a10,a11,...,a1b1;
因 此 总 的 约 数 个 数 之 和 为 ( b 1 + 1 ) ∗ ( b 2 + 1 ) ∗ . . . ∗ ( b n + 1 ) 因此总的约数个数之和为(b1+ 1)*(b2 + 1)*...*(bn+1) (b1+1)(b2+1)...(bn+1);
总 的 约 数 之 和 为 ( a 1 0 + a 1 1 + . . . + a 1 b 1 ) ∗ . . . ∗ ( a n 0 + a n 1 + . . + a n b n ) 总的约数之和为(a_{1}^0 + a_{1}^1 +...+a_1^{b1})*...*(a_{n}^0 + a_{n}^1 +..+a_n^{bn}) (a10+a11+...+a1b1)...(an0+an1+..+anbn);
以 s u m ( a , k ) 代 表 ( a 0 + a 1 + . . . + a k ) 以sum(a, k)代表(a^0 + a ^ 1 + ... + a^k) sum(a,k)(a0+a1+...+ak)
若 k 为 奇 数 , 则 k + 1 为 偶 数 , 有 k + 1 个 约 数 ( 0 − k ) 若k为奇数,则k+1为偶数,有k+1个约数(0-k) k,k+1k+1(0k)
则 s u m ( a , k ) = ( a 0 + . . . + a k − 1 2 ) + ( a k + 1 2 + . . . + a k ) 则sum(a, k) = (a ^0 + ... + a^\frac{k-1}{2}) + (a^\frac{k+1}{2} + ...+ a^k) sum(a,k)=(a0+...+a2k1)+(a2k+1+...+ak)
= ( a 0 + . . . + a k − 1 2 ) + a k + 1 2 ( a 0 + . . . + a k − 1 2 ) =(a ^0 + ... + a^\frac{k-1}{2}) + a^\frac{k+1}{2} (a^0+ ...+ a^\frac{k-1}{2}) =(a0+...+a2k1)+a2k+1(a0+...+a2k1)
提 取 公 因 式 得 s u m ( a , k ) = ( 1 + a k + 1 2 ) ∗ s u m ( a , k − 1 2 ) 提取公因式得sum(a,k) = (1+a^\frac{k+1}{2})*sum(a, \frac{k-1}{2}) sum(a,k)=(1+a2k+1)sum(a,2k1)
若 k 为 偶 数 , 则 k + 1 为 奇 数 , k − 1 为 偶 数 若k为偶数,则k+1为奇数,k-1为偶数 k,k+1,k1
则 s u m ( a , k ) = s u m ( a , k − 1 ) + a k 则sum(a, k) = sum(a, k - 1) + a^k sum(a,k)=sum(a,k1)+ak
代码:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int mod = 9901;
//快速幂
int qmi(int a, int k, int p)  // 求a^k mod p
{
    //这一步不能少
    a %= p;
    int res = 1 % p;
    while (k)
    {
        if (k & 1) res = res * a % p;
        a = a * a % p;
        k >>= 1;
    }
    return res;
}


int sum(int a, int k){
    //结束条件,a^0=1
    if(k == 0) return 1;
    if(k % 2 == 1){
        //k取余2等于1,说明k是奇数,即有偶数个约数
        //sum(a,k) = (1+a^(k+1/2))*sum(a, (k-1/2))
        return (1 + qmi(a, (k + 1) / 2, mod)) * sum(a, (k - 1) / 2) % mod;  
    }
    //sum(a, k) = sum(a, k - 1) + a^k
    return (sum(a, k - 1) + qmi(a, k, mod)) % mod;
}

int main()
{
	int A, B;
    cin >> A >> B;
    //分解质因数
    int res = 1;
    for(int a = 2; a <= A; a ++){
        int k = 0;
        while(A % a == 0){
            A /= a;
            k ++;
        }
        if(k > 0){
            //求解sum(a,k)
            res = res * sum(a, B * k) % mod;
        }
    }
    if(A == 0) res = 0;
    cout << res;
}

前缀和

s [ i ] = ∑ 1 < = j < = i i a [ j ] s[i] = \sum_{1<=j<=i}^{i}a[j] s[i]=1<=j<=iia[j]

差分

d [ 1 ] = a [ 1 ] d[1] = a[1] d[1]=a[1]
d [ i ] = a [ i ] − a [ i − 1 ] ( i > = 2 ) d[i] = a[i] - a[i-1](i >= 2) d[i]=a[i]a[i1](i>=2)
差分还原
a [ i ] = a [ i − 1 ] + d [ i ] a[i] = a[i - 1] + d[i] a[i]=a[i1]+d[i]

二分

前驱二分

while(l < r){
	int mid = (l + r + 1) >> 1;
	if(a[mid] <= x) l = mid; else r = mid - 1;  
}
return a[l];

后继二分

while(l < r){
	int mid = l + r >> 1;
	if(a[mid] >= x) r = mid; else l = mid + 1;
}
return a[l];

实数域二分

//保留k位小数
int k = 3;
double esp = 10^-(k + 2);
while(l + esp < r){
	double mid = (l + r) / 2;
	if(calc(mid)) r = mid; else l = mid;
}

深度优先遍历

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 2 * 1e5 + 10;
int e[N], ne[N], h[N], idx;
int st[N];
int n;

void add(int a, int b){
    e[idx] = b,ne[idx] = h[a], h[a] = idx++; 
}

void dfs(int x){
    //前序遍历
    cout << x;
    st[x] = 1;
    for (int i = h[x]; i != -1; i = ne[i] ){
        int val = e[i];
        if(st[val] == 1) continue;
        dfs(val);
    }
    //后序遍历
    cout << x;
}

int main()
{
    //初始化
    memset(h, -1, sizeof h);
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ){
        int a,b;
        scanf("%d%d", &a, &b);
        add(a, b);
        add(b, a);
    }
    dfs(1);
}

树的深度

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 2 * 1e5 + 10;
int e[N], ne[N], h[N], idx;
int st[N], deep[N];
int n;

void add(int a, int b){
    e[idx] = b,ne[idx] = h[a], h[a] = idx++; 
}

void dfs(int x){
    st[x] = 1;
    for (int i = h[x]; i != -1; i = ne[i] ){
        int val = e[i];
        if(st[val] == 1) continue;
        //子节点深度是父节点的深度+1
        deep[val] = deep[x] + 1;
        dfs(val);
    }
}

int main()
{
    //初始化
    memset(h, -1, sizeof h);
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ){
        int a,b;
        scanf("%d%d", &a, &b);
        add(a, b);
        add(b, a);
    }
    dfs(1);
    cout<< deep[n];
}

树的重心

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 2 * 1e5 + 10;
int e[N], ne[N], h[N], idx;
int st[N], siz[N];
int n, ans = 1e9, pos;

void add(int a, int b){
    e[idx] = b,ne[idx] = h[a], h[a] = idx++; 
}

void dfs(int x){
    st[x] = 1;
    siz[x] = 1;
    int maxNode = 0; //最大子树大小 
    for (int i = h[x]; i != -1; i = ne[i] ){
        int val = e[i];
        if(st[val] == 1) continue;
        dfs(val);
        siz[x] += siz[val];
        maxNode = max(maxNode, siz[val]);
    }
    maxNode = max(maxNode, n - siz[x]);
    if(maxNode < ans){
        ans = maxNode; //最大子节点
        pos = x; //重心
    }
}

int main()
{
    //初始化
    memset(h, -1, sizeof h);
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ){
        int a,b;
        scanf("%d%d", &a, &b);
        add(a, b);
        add(b, a);
    }
    dfs(1);
    cout<< pos;
}

广度优先遍历

在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;
const int N = 2*1e5 + 10;

int e[N], ne[N], h[N], idx;
queue<int> q;
bool st[N];
void add(int a, int b){
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void bfs(){
    memset(st, false, sizeof st);
    queue<int> q;
    q.push(1);
    st[1] = true;
    while (q.size() > 0){
        int x = q.front();
        cout << x << endl;
        q.pop();
        for (int i = h[x]; i != -1; i = ne[i]){
            int j = e[i];
            if(st[j]) continue;
            st[j] = true;
            q.push(j);
        }
    }
}


int main()
{
    memset(h, -1, sizeof h);
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i ++ ){
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b);
    }
    bfs();
}

拓扑排序

在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 1e5 + 10;
int e[N], ne[N], h[N], idx;
int d[N], st[N];

int n,m;

void add(int a, int b){
    //a指向b, b入度+1
    d[b] += 1;
    e[idx] = b, ne[idx]= h[a], h[a] = idx++;
}

void bfs(){
    vector<int> v;
    queue<int> q;
    //入度为0的点入队
    for (int i = 1; i <= n; i ++ ){
        if(d[i] == 0) q.push(i);
    }
    while (q.size() > 0){
        int x = q.front();
        //被访问过的元素打个标记
        st[x] = true; 
        v.push_back(x);
        q.pop();
        for (int i = h[x]; i != -1; i = ne[i] ){
            int y = e[i];
            if(st[y]) continue;
            //该点指向的所有点入度减1
            d[y] -= 1;
            //若入度为0,入队
            if(d[y] == 0) {
                q.push(y);
            }
        } 
    }
    if(v.size() != n) printf("%d", -1);
    else{
        for (int i = 0; i < v.size(); i ++ )printf("%d ", v[i]);
    }
}

int main()
{
    memset(h, -1, sizeof h);
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i ++ ){
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b);
    }
    bfs();
}

拓扑排序判断是否存在环

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值