一些乱七八杂的概念理解 合集

本文深入探讨了数据结构如主席树、线段树的概念与应用,解析了算法原理包括最大流最小割、逆元计算、快速幂运算等,并介绍了离散化技巧,提供了丰富的代码实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如果有错误的话还希望各位巨巨评论指正……

目录

主席树:

线段树:

最大流 = 最小割 :

逆元:

威尔逊定理:

阶乘 :

快速幂:

快速乘:

Pollard Rho+Miller Rabin:大素数判断和素因子分解

一些数论知识:

离散化:


 

主席树:

可以求第K大的数

理解参考:https://www.cnblogs.com/LiuRunky/p/Sustainable_Segment_Tree.html

bin巨的板子放一下嘻嘻 (可以求区间[l,r]内第K小的数)

#include <stdio.h>
#include <math.h>
#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 100010;
const int M = maxn * 30;
int q, n, m, tot;
int a[maxn], t[maxn];
int T[maxn], lson[M], rson[M], c[M];
void Init_hash()
{
    for (int i = 1; i <= n; i++) {
        t[i] = a[i];
    }
    sort(t + 1, t + 1 + n);
    m = unique(t + 1, t + 1 + n) - t - 1;

}
int build(int l, int r)
{
    int root = tot++;
    c[root] = 0;
    if (l != r) {
        int mid = (l + r) >> 1;
        lson[root] = build(l, mid);
        rson[root] = build(mid + 1, r);
    }
    return root;
}
int Hash(int x)
{
    return lower_bound(t + 1, t + 1 + m, x) - t;
}
int update(int root, int pos, int val)
{
    int newroot = tot++, tmp = newroot;
    c[newroot] = c[root] + val;
    int l = 1, r = m;
    while (l < r) {
        int mid = (l + r) >> 1;
        if (pos <= mid) {
            lson[newroot] = tot++;
            rson[newroot] = rson[root];
            newroot = lson[newroot];
            root = lson[root];
            r = mid;
        } else {
            rson[newroot] = tot++;
            lson[newroot] = lson[root];
            newroot = rson[newroot];
            root = rson[root];
            l = mid + 1;
        }
        c[newroot] = c[root] + val;
    }
    return tmp;
}
int query(int left_root, int right_root, int k)
{
    int l = 1, r = m;
    while (l < r) {
        int mid = (l + r) >> 1;
        if (c[lson[left_root]] - c[lson[right_root]] >= k) {
            r = mid;
            left_root = lson[left_root];
            right_root = lson[right_root];

        } else {
            l = mid + 1;
            k -= c[lson[left_root]] - c[lson[right_root]];
            left_root = rson[left_root];
            right_root = rson[right_root];
        }
    }
    return l;
}
int main()
{
    while (~scanf("%d%d", &n, &q)) {
        tot = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
        }
        Init_hash();
        T[n + 1] = build(1, m);
        for (int i = n; i > 0; i--) {
            int pos = Hash(a[i]);
            T[i] = update(T[i + 1], pos, 1);
        }
        while (q--) {
            int l, r, k;
            scanf("%d%d%d", &l, &r, &k);
            printf("%d\n", t[query(T[l], T[r + 1], k)]);
        }
    }
    return 0;
}

线段树:

将数组构建成一棵二叉树,方便于区间查询,单点更新

看网上视频写的板子

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e8 + 5;
int arr[maxn], tree[maxn];
void build(int node, int start, int emd)
{
    if (start == emd) {
        tree[node] = arr[start];
    } else {
        int mid = (start + emd) / 2;
        int left_node = 2 * node + 1;
        int right_node = 2 * node + 2;
        build(left_node, start, mid);
        build(right_node, mid + 1, emd);
        tree[node] = tree[left_node] + tree[right_node];
    }
}

void update(int node, int start, int emd, int idx, int val)
{
    if (start == emd) {
        arr[idx] = val;
        tree[node] = val;
    } else {
        int mid = (start + emd) / 2;
        int left_node = 2 * node + 1;
        int right_node = 2 * node + 2;
        if (idx >= start && idx <= mid) {
            update(left_node, start, mid, idx, val);
        } else {
            update(right_node, mid + 1, emd, idx, val);
        }
        tree[node] = tree[left_node] + tree[right_node];
    }
}

int query(int node, int start, int emd, int L, int R)
{
    if (R < start || L > emd) {
        return 0;
    } else if (L <= start && R >= emd) {
        return tree[node];
    } else if (start == emd) {
        return tree[node];
    } else {
        int mid = (start + emd) / 2;
        int left_node = node * 2 + 1;
        int right_node = node * 2 + 2;
        int sum_left = query(left_node, start, mid, L, R);
        int sum_right = query(right_node, mid + 1, emd, L, R);
        return sum_left + sum_right;
    }
}
int main()
{
    int n, c, k;
    scanf("%d%d%d", &n, &c, &k);
    for (int i = 0; i < n; i++) {
        scanf("%d", &arr[i]);
    }
    build( 0, 0, n - 1);// 建树
    printf("%d\n", query(0, 0, n - 1, 2, 5));
    //查询区间2,5之内的和
    return 0;
}

 

最大流 = 最小割 :

从A地到B地有很多条路可以走 并且 每条路可能会有宽窄不同的地方(即权值不同)最大流就是从A地到B地 可以通过的最大流量的和 因为到比较窄的地方 就只能通过那么一些流量 即时后来路变宽了 还是那么点流量…… 

从最小割的理解看就是 使割掉的边的权值最小 然后使两点间没有通路 的边权之和

 

逆元:

\frac{a}{b}\, mod\: p 因为 分数无法取模 这时候需要用到逆元 inv[b] 是 b 关于 p 的逆元 所以

 \frac{a}{b}\, mod\: p =(a *inv[b])mod\, p

 

威尔逊定理:

当且仅当p为素数时, (p - 1)! ≡ (-1) mod p 

 

阶乘

N!= N*(N-1)*(N-2)……*2*1 注意 0!=1

 

快速幂:

通过二进制转换,将乘的次数分解,减少次数消耗的时间

 

快速乘:

与快速幂相似,比正常乘法快一些,也避免乘法过程中爆int / long long范围

 

Pollard Rho+Miller Rabin:大素数判断和素因子分解

//判断素数
bool Miller_Rabin(LL n)
{
    if(n == 2) return true;
    if(n < 2 || !(n & 1)) return false;
    LL m = n - 1;
    int k = 0;
    while((m & 1) == 0)
    {
        k++;
        m >>= 1;
    }
    for(int i=0; i<Times; i++)
    {
        LL a = rand() % (n - 1) + 1;
        LL x = quick_mod(a, m, n);
        LL y = 0;
        for(int j=0; j<k; j++)
        {
            y = multi(x, x, n);
            if(y == 1 && x != 1 && x != n - 1) return false;
            x = y;
        }
        if(y != 1) return false;
    }
    return true;
}
//分解素数
LL pollard_rho(LL n, LL c)
{
    LL i = 1, k = 2;
    LL x = rand() % (n - 1) + 1;
    LL y = x;
    while(true)
    {
        i++;
        x = (multi(x, x, n) + c) % n;
        LL d = gcd((y - x + n) % n, n);
        if(1 < d && d < n) return d;
        if(y == x) return n;
        if(i == k)
        {
            y = x;
            k <<= 1;
        }
    }
}

一些数论知识:

https://zerol.me/2018/08/29/number-theory/

 

离散化:

将范围较大的有限个数据变成较小的数,使数据中的最大值尽可能小且保证所有数据都是正数。

理解参考:https://blog.youkuaiyun.com/qq_41754350/article/details/81199993

栗子:12342434 12312 234234 12342434

可以离散化变成 3 1 2 3

数据一下子变小了 比较也更容易

板子如下:

#include<algorithm> 
#include <bits/stdc++.h>
//n 原数组大小 num 原数组中的元素 lsh 离散化的数组 cnt 离散化后的数组大小
using namespace std;
int main()
{ 
    int lsh[MAXN] , cnt , num[MAXN] , n;
    for(int i=1; i<=n; i++) {
	    scanf("%d",&num[i]);
    	lsh[i] = num[i];	
    }
    sort(lsh+1 , lsh+n+1);
    cnt = unique(lsh+1 , lsh+n+1) - lsh - 1;
    for(int i=1; i<=n; i++)
	    num[i] = lower_bound(lsh+1 , lsh+cnt+1 , num[i]) - lsh;
    for(int i=1; i<=n; i++)
        printf("%d ",num[i]);
    return 0;
}

在某个博客上看到了很神奇的操作

暂时没看明白 先贴上代码emm

struct Node{
int val, order;
}node[maxn];
 
for(int i=1; i<=n; i++) {
    if(i==1 || node[i].val!= node[i-1].val) { //去重
        r[node[i].order] = i;
    }
}

来源:https://blog.youkuaiyun.com/qq_41754350/article/details/81199993

 

 


先酱~~

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值