板子合集2.0 md文本 可自助打印

效果链接:板子合集2.0-CSDN博客

可以在微软商店等下载免费的vscode,然后下载对应插件(参考:在VSCode中使用MarkDown_vscode markdown-CSDN博客

然后直接复制下文去打印即可。

大部分我都有写博客,可以去博客收集更多信息和图片等。❤️


# DBWGLX's板子2.0

                       

原文链接:https://blog.csdn.net/JK01WYX/

[toc]

# 1.快速幂板子;快速幂求逆元

## 原理:

我们用分治思想是比一个一个乘快的

即比如我们求a的8次方 :a1*a1 = a2 ,那么我们直接a2*a2 = a4,a4*a4 = a8

参数就是几次幂。返回值是对应参数的幂 (这里对p取余了)(一般也把a当参数)

```

long long a, p;//a^c mod p=s

long long fp(int c)

{

    if(c==0)return 1;

    if (c == 1)return a % p;


 

    long long tmp = fp(c / 2);

    if (c % 2 == 0)

        return tmp * tmp % p;

    else

        return tmp * tmp % p * a % p;

}

long long fp(long long a,int c)

{

    if(c==0)return 1;

    if (c == 1)return a % MOD;

    long long tmp = fp(a,c / 2);

    if (c % 2 == 0)

        return tmp * tmp % MOD;

    else

        return tmp * tmp % MOD * a % MOD;

}

```

细节解释:

c == 1就是1次幂。

tmp就是a的c/2次幂。我们要返回c次幂,整数除法是向下取整的。

(比如5次幂,5/2==2,那么需要额外乘一个使得为c次幂)  

——————————

非递归写法:(二进制拆分)

```

#define ll long long

ll mod = 1e9+7;

ll q(ll a,ll b)

{

    ll s = 1;

    while(b)

    {

        if(b&1) s = s * a %mod;

        a = a * a % mod;

        b >> 1;

    }

    return s;

}

```

———————

## 快速幂求逆元:

前提:

**费马小定理:**  $$1 ≡ a^{p-1}  ~(mod  ~p) $$    ( 1和 a^(p-1)在mod质数p下同余 )


 

[欧拉定理 & 费马小定理 - OI Wiki](https://oi-wiki.org/math/number-theory/fermat/)

————

逆元也就是倒数,若我们求a的逆元x,则满足:

$$ax ≡ 1~ (mod ~p)$$

而由费马小定理可得

$$ax ≡ a^{p-1}~ (mod ~p)$$

$$x ≡ a^{p-2}~ (mod ~p)$$


 

# 2.gcd得最大公约数

证法一

a可以表示成a = kb + r(a,b,k,r皆为正整数,且r不为0)

假设d是a,b的一个公约数,记作d|a,d|b,即a和b都可以被d整除。

而r = a - kb,两边同时除以d,r/d=a/d-kb/d,由等式右边可知m=r/d为整数,因此d|r

因此d也是b,a mod b的公约数。

因(a,b)和(b,a mod b)的公约数相等,则其最大公约数也相等,得证。

——————

百度百科证法一的一些便于理解的细节:

>我们求 a 和 b 的最大公约数。

**(如果a是b的倍数,那么b就是最大公约数。)**

a>b,a可以表示为 a = kb + r

**设d为a和b的最大公约数**

对上式等号左右两端同时除以d,得 a/d = kb/d + r/d

a/d 和 kb/d都是整数,那么r/d也是整数。那么r也是d的倍数。同时r<b,**r与b的最大公约数也是d**

( r<d是因为r = a%b  (由a的表示可知))

那么问题就转化成求 b 与 r 的最大公约数。

>即 gcd(a,b) = gcd(b,a mod b)

r会一直变小,为0时,**b就是最大公约数了**        (b最小为1,当b为1时,余数必然为0)

——————

(当然再进一次循环就是 a 与 0 。返回a即可):

``````

long long gcd(long long a,long long b)

{

    if (a<b)

        swap(a,b);

    if (b==0)      

        return a;

    else

        return gcd(b, a % b);

}

``````

# 3.堆优化的dijkstra板子

1.第一个结构体dis_node把下标和该下标距起点的当前最短路放入一个结构体了,这样做堆排的时候方便找下标。

2.cmp仿函数是用于堆排比较器的,(试过greater<自己的类型>()是不行的。。)

建的小根堆快速选择当前最短路(dijkstra的原理)

3.

①tmpdis代表节点之间的距离,有的时候就是1,自己设置吧,dijkstr主函数中算距离用。

②dij代表此点到所有点的最短路,所以我放参数列表当输出型参数了,许多题可能要用到。

③vector<set<int>>arr代表通路,用set可以去重(有的题可能是多重图,即含平行边)。

④加强:自己到自己不用,所以if一下。因为next = cur,会吧dij[cur]给覆盖掉造成错误

``````

    int tmpdis = 1;

    struct dis_node//放堆里面比长度,但是想知道端点

    {

        int dis;

        int next;

        bool operator < (const dis_node& a)

        {

            return dis < a.dis;

        }

        dis_node(int d, int n)

        {

            dis = d; next = n;

        }

    };

    class cmp

    {

    public:

        bool operator()(dis_node a, dis_node b)

        {

            return a.dis > b.dis;//

        }

    };

    void dijkstr(vector<int>&dij,vector<set<int>>& arr, int ori, int n)

    {

        priority_queue<dis_node, vector<dis_node>, cmp>heap;

       

        vector<int>barr(n + 1);

        int cur = ori;

        while (1)

        {

            //该次点所有可走的

            for (auto next : arr[cur])

            {

                if(next == cur)continue;

                //next就是下一个点(邻接表

                if (dij[next] == 0)

                    dij[next] = dij[cur] + tmpdis;

                else

                    dij[next] = min(dij[next], dij[cur] + tmpdis);

                heap.push(dis_node(dij[next], next));

            }

            //该点已使用,已最短,无需再抵达

            barr[cur] = 1;

            //最短路中找最短,同时可抵达的

            while (heap.size())

            {

                if (barr[heap.top().next] == 0)

                    break;

                heap.pop();

            }

            if (heap.size() == 0)break;

            cur = heap.top().next;

            heap.pop();

        }

    }

``````

# 4.【线段树】框架

注意,线段树节点是从1开始的,维护的区间可以不是。

构造里可以改build_tree范围。

ll构造:

```

class ST//segment tree

{

    struct node

    {

        ll val;

        int l, m, r;

        node(int v = 0) :val(v), l(0), m(0), r(0)

        {}

    };

    int aiml, aimr;

    int val;

    int n;

    vector<ll>a;

    vector<node>d;

    void build_tree(int i, int l, int r)

    {

        d[i].l = l, d[i].m = l + (r - l) / 2, d[i].r = r;

        if (l == r)

        {

            d[i].val = a[l] ;

            return;

        }

        build_tree(i * 2, l, d[i].m);

        build_tree(i * 2 + 1, d[i].m + 1, r);

        push_up(i);

    }

    void push_up(int i)

    {

        d[i].val = (d[i * 2].val + d[i * 2 + 1].val) ;

    }

    void push_down(int i)

    {

        auto& lchild = d[i * 2], & rchild = d[i * 2 + 1];

    }

    ll _getsum(int i)

    {

        if (d[i].l > aimr || d[i].r < aiml)return 0;

        if (aiml <= d[i].l && d[i].r <= aimr)return d[i].val;

        //push_down(i);

        return (_getsum(i * 2) + _getsum(i * 2 + 1)) ;

    }

    ll _getmax(int i)

    {

        if (d[i].l > aimr || d[i].r < aiml)return LLONG_MIN;

        if (aiml <= d[i].l && d[i].r <= aimr)return d[i].val;

        //push_down(i);

        return max(_getmax(i * 2), _getmax(i * 2 + 1));

    }

    void _update1(int i)

    {

        if (d[i].l > aimr || d[i].r < aiml)return;

        if (aiml <= d[i].l && d[i].r <= aimr)

        {

            d[i].val = (d[i].val + val * (d[i].r - d[i].l + 1));

            return;

        }

        //push_down(i);//因为要向下搜了,所以把当前的懒标记给下面兑现一下

        _update1(i * 2);

        _update1(i * 2 + 1);

        //push_up(i);

    }

public:

    ll getsum(int l, int r)

    {

        aiml = l, aimr = r;

        return _getsum(1);

    }

    ll getmax(int l, int r)

    {

        aiml = l, aimr = r;

        return _getmax(1);

    }

    void update1(int l, int r, ll val)

    {

        aiml = l, aimr = r;

        this->val = val;

        _update1(1);//加并挂标记

    }

    ST(vector<ll>arr)

    {

        a = arr;

        n = a.size() - 1;

        d = vector<node>(4 * n);

        build_tree(1, 1, n);

        a = {};//清空a数组

    }

};

```

# 5.线段树的三个板子

## 线段树1板子 区间加

类的构造函数写在类最后了,本板子没有将左右下标封装到节点中,而是实时计算的。

建议阅读:线段树 - OI Wiki (oi-wiki.org)

### 线段树的结构关系:

这是一颗线段树,a[5] = {10,11,12,13,14}

![alt text](image.png)

可以看出5个节点对应下图二叉树的话,其实节点到了第4层:

``````

//            1

//    2      3      2^1

//  4   5  6   7        2^2

//     8 9          2^3

//1     log1==0

//2     log2==1     log3==1

//4     log4==2     log5==log6==log7==2

//8

//log5 == 2, +1在第三层,0~3 共四层 4

//

//完全二叉树的总结点数就是:

//等比数列求和

//1*(2^  - 1)/(2-1)

//logn + 1层

//pow(2,(int)log2(n)+1)-1

//

``````

### long long作为下标的代码:

``````

#define ll long long

template<class T>

class ST//segment tree

{

    struct node

    {

        T val;

        T t;//懒标记//服务后代

        node(T v = 0) :val(v), t(0)

        {}

    };

    ll n = a.size();

    vector<T>a;

    vector<node>d;

public:

    void build_tree(ll i, ll l, ll r)

    {

        if (l == r)

        {

            d[i].val = a[l];

            return;

        }

        ll mid = l + (r - l) / 2;

        build_tree(i * 2, l, mid);

        build_tree(i * 2 + 1, mid + 1, r);

        d[i].val = d[i * 2].val + d[i * 2 + 1].val;

    }

    void spread(ll i, ll l, ll r, ll aiml, ll aimr)

    {

        ll mid = l + (r - l) / 2;

        if (d[i].t != 0 && l != r)

        {

            d[i * 2].val += d[i].t * (mid - l + 1);

            d[i * 2 + 1].val += d[i].t * (r - mid);

            d[i * 2].t += d[i].t;//可能上上次也没改

            d[i * 2 + 1].t += d[i].t;

            d[i].t = 0;

        }

    }

    T getsum(ll l, ll r)

    {

        return _getsum(1, 1, n, l, r);

    }

    T _getsum(ll i, ll l, ll r, ll aiml, ll aimr)

    {

        if (aiml <= l && r <= aimr)//查询区间的子集全部加起来

            return d[i].val;

        //访问

        ll mid = l + (r - l) / 2;

        spread(i, l, r, aiml, aimr);

        T ret = 0;

        if (aiml <= mid)

            ret += _getsum(i * 2, l, mid, aiml, aimr);

        if (aimr >= mid + 1)

            ret += _getsum(i * 2 + 1, mid + 1, r, aiml, aimr);

        return ret;

    }

    void update(ll l, ll r, ll val)

    {

        _update(1, 1, n, l, r, val);//加并挂标记

    }

    void _update(ll i, ll l, ll r, ll aiml, ll aimr, ll val)

    {

        if (aiml <= l && r <= aimr)

        {

            d[i].val += val * (r - l + 1);

            d[i].t += val;

            return;

        }

        ll mid = l + (r - l) / 2;

        spread(i, l, r, aiml, aimr);

        if (aiml <= mid)

            _update(i * 2, l, mid, aiml, aimr, val);

        if (aimr >= mid + 1)

            _update(i * 2 + 1, mid + 1, r, aiml, aimr, val);

        //我们只对叶子更新了,(别多想懒标记)

        d[i].val = d[i * 2].val + d[i * 2 + 1].val;

    }

    ST(vector<T>arr)

    {

        a = arr;

        n = a.size() - 1;

        d = vector<node>(pow(2, (ll)log2(n) + 1 + 1) - 1 + 1);

        build_tree(1, 1, n);

    }

};

``````

## 线段树2板子 区间加与乘

当对区间即有加操作,又有乘操作时:

``````

//乘法满足分配率!!,所以乘懒标记可以“攻击”加懒标记

//策略:两个标记都安排

//当乘标记来临时,对自己和懒标记都乘//假设都没有向后延伸

//

//(特别好的分析:)

//当加标记来临时,正常加就好啦,因为乘已经对加处理啦

//

//两个一起来临呢,先乘!!!!!!!!!!!!!!!!!

//(乘已经对这部分加处理过了)

``````

``````

template<class T>

class ST//segment tree

{

    struct node

    {

        T val;

        T t1;

        T t2;//乘

        node(T v = 0) :val(v), t1(0), t2(1)//x2x3 == x6

        {}

    };

    ll n = a.size();

    vector<T>a;

    vector<node>d;

public:

    void build_tree(ll i, ll l, ll r)

    {

        if (l == r)

        {

            d[i].val = a[l] % MOD;

            return;

        }

        ll mid = l + (r - l) / 2;

        build_tree(i * 2, l, mid);

        build_tree(i * 2 + 1, mid + 1, r);

        d[i].val = d[i * 2].val + d[i * 2 + 1].val;

    }

    void spread(ll i, ll l, ll r, ll aiml, ll aimr)

    {

        //懒惰

        ll mid = l + (r - l) / 2;

        //if ((d[i].t1 != 0 || d[i].t2 != 1) && l != r)

        //{

        T t1 = d[i].t1, t2 = d[i].t2;

        //先乘

        d[i * 2].val = (d[i * 2].val * t2) % MOD;

        d[i * 2].t1 = (d[i * 2].t1 * t2) % MOD;

        d[i * 2].t2 = (d[i * 2].t2 * t2) % MOD;

        d[i * 2 + 1].val = (d[i * 2 + 1].val * t2) % MOD;

        d[i * 2 + 1].t1 = (d[i * 2 + 1].t1 * t2) % MOD;

        d[i * 2 + 1].t2 = (d[i * 2 + 1].t2 * t2) % MOD;

        //后加

        d[i * 2].val = (d[i * 2].val + t1 * (mid - l + 1)) % MOD;

        d[i * 2 + 1].val = (d[i * 2 + 1].val + t1 * (r - mid)) % MOD;

        d[i * 2].t1 = (d[i * 2].t1 + t1) % MOD;

        d[i * 2 + 1].t1 = (d[i * 2 + 1].t1 + t1) % MOD;

        //复原

        d[i].t1 = 0;

        d[i].t2 = 1;

        //}

        ///

    }

    ll getsum(ll l, ll r)

    {

        return _getsum(1, 1, n, l, r);

    }

    ll _getsum(ll i, ll l, ll r, ll aiml, ll aimr)

    {

        if (aiml <= l && r <= aimr)

            return d[i].val;

        ll mid = l + (r - l) / 2;

        spread(i, l, r, aiml, aimr);

        ll ret = 0;

        if (aiml <= mid)

            ret = (ret + _getsum(i * 2, l, mid, aiml, aimr)) % MOD;

        if (aimr >= mid + 1)

            ret = (ret + _getsum(i * 2 + 1, mid + 1, r, aiml, aimr)) % MOD;

        return ret;

    }

    void update1(ll l, ll r, ll val)

    {

        _update1(1, 1, n, l, r, val);//加并挂标记

    }

    void _update1(ll i, ll l, ll r, ll aiml, ll aimr, T val)

    {

        if (aiml <= l && r <= aimr)

        {

            d[i].t1 = (d[i].t1 + val) % MOD;

            d[i].val = (d[i].val + val * (r - l + 1)) % MOD;

            return;

        }

        ll mid = l + (r - l) / 2;

        spread(i, l, r, aiml, aimr);

        if (aiml <= mid)

            _update1(i * 2, l, mid, aiml, aimr, val);

        if (aimr >= mid + 1)

            _update1(i * 2 + 1, mid + 1, r, aiml, aimr, val);

        d[i].val = (d[i * 2].val + d[i * 2 + 1].val) % MOD;

    }

    void update2(ll l, ll r, T val)

    {

        _update2(1, 1, n, l, r, val);//加并挂标记

    }

    void _update2(ll i, ll l, ll r, ll aiml, ll aimr,T val)

    {

        if (aiml <= l && r <= aimr)

        {

            d[i].val = (d[i].val * val) % MOD;

            d[i].t1 = (d[i].t1 * val) % MOD;

            d[i].t2 = (d[i].t2 * val) % MOD;

            return;

        }

        ll mid = l + (r - l) / 2;

        spread(i, l, r, aiml, aimr);

        if (aiml <= mid)

            _update2(i * 2, l, mid, aiml, aimr, val);

        if (aimr >= mid + 1)

            _update2(i * 2 + 1, mid + 1, r, aiml, aimr, val);

        d[i].val = (d[i * 2].val + d[i * 2 + 1].val) % MOD;

    }

    ST(vector<T>arr)

    {

        a = arr;

        n = a.size() - 1;

        d = vector<node>(pow(2, (ll)log2(n) + 1 + 1) - 1 + 1);

        build_tree(1, 1, n);

    }

};

``````

## 线段树3板子 区间最值操作、区间历史最值

函数递归时,l,r可以存入节点node结构体中。递归时,相同的aiml,aimr,val,直接用类成员变量即可,第一次调用时及时修改。

(貌似在O(2)优化下这些修改并没有真正优化。)

因为最大值用了单独的懒标记t3,所以t3需要在区间乘的时候单独乘一下。

```

#include<bits/stdc++.h>

using namespace std;

#define ll long long

#define endl "\n"

#define PII pair<int,int>

#define int long long

const int maxn = 5e5 + 5;

const int minv = -5e18;

int MOD = 1e17;

class ST//segment tree

{

    struct node

    {

        ll val;//区间和

        ll maxa;//区间最大值

        ll seca;//区间次大值

        ll maxb;//区间历史最大值

        ll maxn;//最大值数目 用于最大值修改

        int t1;//加懒标记

        int maxt1;//不是最大值也要统计maxb,也需要最大懒标记

        int t2;//乘懒标记

        int t3;//最大值专属懒标记 VIP

        int maxt3;//期间最大加懒标记

        int l, m, r;

        node(int v = 0) :val(v), maxa(0), seca(-2e9), maxb(0), maxn(1),

            t1(0), maxt1(0), t2(1), t3(0), maxt3(0), l(0), m(0), r(0)

        {}

    };

    int aiml, aimr;

    int val;

    int n = a.size();

    vector<ll>a;

    vector<node>d;

    void build_tree(int i, int l, int r)

    {

        d[i].l = l, d[i].m = l + (r - l) / 2, d[i].r = r;

        if (l == r)

        {

            d[i].val = d[i].maxa = d[i].maxb = a[l] ;

            return;

        }

        build_tree(i * 2, l, d[i].m);

        build_tree(i * 2 + 1, d[i].m + 1, r);

        push_up(i);

    }

    void push_up(int i)

    {

        d[i].val = (d[i * 2].val + d[i * 2 + 1].val) ;

        d[i].maxa = max(d[i * 2].maxa, d[i * 2 + 1].maxa);

        d[i].maxb = max(d[i * 2].maxb, d[i * 2 + 1].maxb);

        //不过确实有多个最大值就错了

        if (d[i * 2].maxa == d[i * 2 + 1].maxa)

        {

            d[i].seca = max(d[i * 2].seca, d[i * 2 + 1].seca);

            d[i].maxn = d[i * 2].maxn + d[i * 2 + 1].maxn;

        }

        else if (d[i * 2].maxa > d[i * 2 + 1].maxa)

        {

            d[i].seca = max(d[i * 2].seca, d[i * 2 + 1].maxa);

            d[i].maxn = d[i * 2].maxn;

        }

        else// if (d[i * 2].maxa < d[i * 2 + 1].maxa)

        {

            d[i].seca = max(d[i * 2].maxa, d[i * 2 + 1].seca);

            d[i].maxn = d[i * 2 + 1].maxn;

        }

    }

    void change(int i, int t1, int maxt1, int t2, int t3, int maxt3)

    {

        auto& self = d[i];

        //self.val = self.val * t2%MOD;

        //self.maxa = self.maxa * t2;

        //self.seca = self.seca * t2;

        //self.t1 = self.t1*t2%MOD;

        //self.t3 = self.t3 * t2 % MOD;

        //self.t2 = self.t2*t2%MOD;

        self.val = ((self.val + t1 * (d[i].r - d[i].l + 1 - self.maxn) )  + t3 * self.maxn) ;

        self.maxb = max(self.maxb, self.maxa + maxt3);

        self.maxa += t3;

        if (self.seca != -2e9)

            self.seca += t1;

        self.maxt1 = max(self.maxt1, self.t1 + maxt1);

        self.t1 = (self.t1 + t1) ;

        self.maxt3 = max(self.maxt3, self.t3 + maxt3);

        self.t3 = (self.t3 + t3) ;

    }

    void push_down(int i)

    {

        //懒惰

        int t1 = d[i].t1, maxt1 = d[i].maxt1, t2 = d[i].t2, t3 = d[i].t3, maxt3 = d[i].maxt3;

        auto& lchild = d[i * 2], & rchild = d[i * 2 + 1];

        int maxatmp = max(lchild.maxa, rchild.maxa);

        if (lchild.maxa == maxatmp)

            change(i * 2, t1, maxt1, t2, t3, maxt3);

        else

            change(i * 2, t1, maxt1, t2, t1, maxt1);//没专属

        if (rchild.maxa == maxatmp)

            change(i * 2 + 1, t1, maxt1, t2, t3, maxt3);

        else

            change(i * 2 + 1, t1, maxt1, t2, t1, maxt1);

        //复原

        d[i].t1 = d[i].maxt1 = d[i].maxt3 = d[i].t3 = 0;

        d[i].t2 = 1;

    }

    ll _getsum(int i)

    {

        if (d[i].l > aimr || d[i].r < aiml)return 0;

        if (aiml <= d[i].l && d[i].r <= aimr)return d[i].val;

        push_down(i);

        return (_getsum(i * 2) + _getsum(i * 2 + 1)) ;

    }

    ll _getmaxa(int i)

    {

        if (d[i].l > aimr || d[i].r < aiml)return LLONG_MIN;

        if (aiml <= d[i].l && d[i].r <= aimr)return d[i].maxa;

        push_down(i);

        return max(_getmaxa(i * 2), _getmaxa(i * 2 + 1));

    }

    ll _getmaxb(int i)

    {

        if (d[i].l > aimr || d[i].r < aiml)return LLONG_MIN;

        if (aiml <= d[i].l && d[i].r <= aimr)return d[i].maxb;

        push_down(i);

        return max(_getmaxb(i * 2), _getmaxb(i * 2 + 1));

    }

    void _update1(int i)

    {

        if (d[i].l > aimr || d[i].r < aiml)return;

        if (aiml <= d[i].l && d[i].r <= aimr)

        {

            d[i].val = (d[i].val + val * (d[i].r - d[i].l + 1));

            d[i].maxa = d[i].maxa + val;

            d[i].maxb = max(d[i].maxb, d[i].maxa);

            if (d[i].seca != -2e9)

                d[i].seca += val;

            d[i].t1 = (d[i].t1 + val);

            d[i].t3 = (d[i].t3 + val);

            d[i].maxt1 = max(d[i].maxt1, d[i].t1);

            d[i].maxt3 = max(d[i].maxt3, d[i].t3);

            return;

        }

        push_down(i);//因为要向下搜了,所以把当前的懒标记给下面兑现一下

        _update1(i * 2);

        _update1(i * 2 + 1);

        push_up(i);

    }

    void _update2(int i)

    {

        if (d[i].l > aimr || d[i].r < aiml)return;

        if (aiml <= d[i].l && d[i].r <= aimr)

        {

            d[i].val = (d[i].val * val);

            d[i].t1 = (d[i].t1 * val);

            d[i].t3 = (d[i].t3 * val);

            d[i].t2 = (d[i].t2 * val);

            return;

        }

        push_down(i);

        _update2(i * 2);

        _update2(i * 2 + 1);

        push_up(i);

    }

    void _update_min(int i)

    {

        if (d[i].l > aimr || d[i].r < aiml || d[i].maxa <= val)return;

        if (aiml <= d[i].l && d[i].r <= aimr && d[i].seca < val)

        {

            int k = d[i].maxa - val;

            d[i].val -= d[i].maxn * k;

            d[i].maxa = val;

            d[i].t3 -= k;

            return;

        }

        push_down(i);

        _update_min(i * 2);

        _update_min(i * 2 + 1);

        push_up(i);

    }

public:

    ll getsum(int l, int r)

    {

        aiml = l, aimr = r;

        return _getsum(1);

    }

    ll getmaxa(int l, int r)

    {

        aiml = l, aimr = r;

        return _getmaxa(1);

    }

    ll getmaxb(int l, int r)

    {

        aiml = l, aimr = r;

        return _getmaxb(1);

    }

    void update1(int l, int r, ll val)

    {

        aiml = l, aimr = r;

        this->val = val;

        _update1(1);//加并挂标记

    }

    void update2(int l, int r, ll val)

    {

        aiml = l, aimr = r;

        this->val = val;

        _update2(1);//加并挂标记

    }

    void update_min(int l, int r, ll val)

    {

        aiml = l, aimr = r;

        this->val = val;

        _update_min(1);

    }

    ST(vector<ll>arr)

    {

        a = arr;

        n = a.size() - 1;

        d = vector<node>(4 * n);

        build_tree(1, 1, n);

        a = {};//清空a数组

    }

};

//区间加完,子区间最大值也加

//交汇部分需要再比比

signed main()

{

    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);

    int n, m;

    cin >> n >> m;

    vector<int>arr(1 + n);

    for (int i = 1; i <= n; i++)

        cin >> arr[i];

    ST demo(arr);

    for (int i = 0; i < m; i++)

    {

        int op, l, r, val;

        cin >> op >> l >> r;

        switch (op)

        {

        case 1:

            cin >> val;

            demo.update1(l, r, val);

            break;

        case 2:

            cin >> val;

            demo.update_min(l, r, val);

            break;

        case 3:

            cout << demo.getsum(l, r) << endl;

            break;

        case 4:

            cout << demo.getmaxa(l, r) << endl;

            break;

        case 5:

            cout << demo.getmaxb(l, r) << endl;

            break;

        }

    }

    return 0;

}

```

---

# 6.树状数组1板子 可修改单个点的前缀和

![请添加图片描述](https://img-blog.csdnimg.cn/direct/40119244e1ce46d4835b278e17bfa617.png)


 

树状数组中,规定 c[x] 管辖的区间长度为 $2^{k}$,其中:

设二进制最低位为第 0 位,则 k 恰好为 x 二进制表示中,最低位的 1 所在的二进制位数;

2^k(c[x] 的管辖区间长度)恰好为 x 二进制表示中,最低位的 1 以及后面所有 0 组成的数。

 对于lowbit的证明,注意正数的原码反码和补码是相同的。

add函数给该节点增加值后,while循环一直找父节点加值。

```

template<class T>

class BIT//Binary Indexed Tree

{

public:

    ll n;//a的大小

    vector<T>a;//原数组

    vector<T>c;//树状数组

    ll lowbit(ll a)

    {

        return a & -a;

    }

    T getsum(ll i)

    {

        T sum = 0;

        while (i > 0)

        {

            sum += c[i];

            i -= lowbit(i);

        }

        return sum;

    }

    void add(ll i, T v)

    {

        while (i <= n)

        {

            c[i] += v;

            i = i + lowbit(i);

        }

    }

    BIT(vector<T>_a)

    {

        a = _a;

        n = a.size() - 1;

        c = vector<T>(n + 1);

        //直接把树建好

        for (ll i = 1; i <= n; i++)

        {

            add(i, a[i]);

        }

    }

};

```

# 7.树状数组2板子 差分数组快速区间加 求某个数

add可以给一个数加值

如果想给一段区间加值,有没有更快的操作:add1

实际上,这个是差分数组,差分的前缀和就是这个位置的数值,所以是用来求单个数的

```

template<class T>

class BIT//Binary Indexed Tree

{

public:

    ll n;//a的大小

    vector<T>a;//原数组

    vector<T>c;//树状数组//还是求和,只不过传过来的是差分了

    ll lowbit(ll a)

    {

        return a & -a;

    }

    T getsum(ll i)

    {

        T sum = 0;

        while (i > 0)

        {

            sum += c[i];

            i -= lowbit(i);

        }

        return sum;

    }

    void add(ll i, ll v)

    {

        while (i <= n)

        {

            c[i] += v;

            i += lowbit(i);

        }

    }

    void add1(ll l, ll r, ll v)

    {

        add(l, v);

        add(r + 1, -v);

    }

    BIT(vector<T>_a)

    {

        a = _a;

        n = a.size() - 1;

        c = vector<T>(n + 1);

        //直接把树建好

        for (ll i = 1; i <= n; i++)

        {

            add(i, a[i]);

        }

    }

};

```

使用示范:

``````

//差分构造:

void solve()

{

    int n, m;

    cin >> n >> m;

    vector<int>arr(n + 1);

    for (int i = 1; i <= n; i++)

    {

        cin >> arr[i];

    }

    vector<int>d(n + 1);

    d[1] = arr[1];

    for (int i = 2; i <= n; i++)

    {

        d[i] = arr[i] - arr[i - 1];

    }

    BIT<int> demo(d);

    /// <summary>

    for (int i = 1; i <= m; i++)

    {

        int op;

        cin >> op;

        if (op == 1)

        {

            int x, y, k;

            cin >> x >> y >> k;

            demo.add1(x, y, k);

        }

        else if (op == 2)

        {

            int x;

            cin >> x;

            cout << demo.getsum(x) << endl;

        }

    }

}

``````

# 8.manacher板子 快速求最长回文串的长度

## 原理

r记录当前最右的回文(l(左)与之对应),这样我们后来在r中偏右进行判断时,因为l r之间是回文,所以可以参照中偏左对应的位置,少判断许多次。

## 使用示范,本板子是加#(奇偶长度一起算)的:

d[i]表示以位置i为中心的最长回文串的半径长度

d数组的值-1即是本位置最长回文长度,原因看最下面注释。

``````

void solve()

{

    string str, aim;

    cin >> str;

    aim += "#";

    for (int i = 0; i < str.size(); i++)

    {

        aim += str[i];

        aim += "#";

    }

    vector<int>d(aim.size());

    //  abcba

    auto manacher = [&](string s) {

        int l = 0, r = -1;

        for (int i = 0; i < s.size(); i++)

        {

            int k = i > r ? 1 : min(d[l + r - i], r - i + 1);//左边对称相同,但不越界

            //k是半径,加了等于下一位

            //朴素算法

            while (i - k >= 0 && i + k <= s.size() && s[i - k] == s[i + k])

                k++;

            d[i] = k;

            if (i + d[i] - 1 > r)

            {

                r = i + d[i] - 1;

                l = i - d[i] + 1;

            }

        }

    };

    manacher(aim);

    int ans = 0;

    for (auto x : d)

    {

        ans = max(ans, x);

    }

    //d[i]表示i(不包括i)左右的对称的长度

    // # a # b # a #

    //       - - - -

    // # a # b # b # a #

    //         - - - - -

    cout << ans - 1;

}

``````

## 单独lamda:

 ``````

    //  abcba

    auto manacher = [&](string s) {

        int l = 0, r = -1;

        for (int i = 0; i < s.size(); i++)

        {

            int k = i > r ? 1 : min(d[l + r - i], r - i + 1);//左边对称相同,但不越界

            //k是半径,加了等于下一位

            //朴素算法

            while (i - k >= 0 && i + k <= s.size() && s[i - k] == s[i + k])

                k++;

            d[i] = k;

            if (i + d[i] - 1 > r)

            {

                r = i + d[i] - 1;

                l = i - d[i] + 1;

            }

        }

    };

``````

————

## OI Wiki摘录的只算单数和双数的:

``````

vector<int> d1(n);

for (int i = 0, l = 0, r = -1; i < n; i++) {

  int k = (i > r) ? 1 : min(d1[l + r - i], r - i + 1);

  while (0 <= i - k && i + k < n && s[i - k] == s[i + k]) {

    k++;

  }

  d1[i] = k--;

  if (i + k > r) {

    l = i - k;

    r = i + k;

  }

}

``````

``````

vector<int> d2(n);

for (int i = 0, l = 0, r = -1; i < n; i++) {

  int k = (i > r) ? 0 : min(d2[l + r - i + 1], r - i + 1);

  while (0 <= i - k - 1 && i + k < n && s[i - k - 1] == s[i + k]) {

    k++;

  }

  d2[i] = k--;

  if (i + k > r) {

    l = i - k - 1;

    r = i + k;

  }

}

``````

# 9.ST表板子 类似归并的有条理暴力 sparse-table

1.原理是“倍增”,直到两个长度为1的就可以合成一个长度为2的,两个2合成4,两个4合成8。

2.最后使用时没必要追求“正好匹配”,可以在范围内取多点:

比如看4~8长度为5(45678),我们取长度为4,看4~7与5~8的最大值哪个更大即可。

## ST表部分的代码:

``````

//ST表

    vector<vector<int>>st(30,vector<int>(n+1));//len = 2的i次方

    int len = 1;

    for (int pow = 0; len <= n; pow++, len *=2)

    {

        for (int left = 1; left <= n- len+1; left++)

        {

            if (pow == 0)

                st[0][left] = arr[left];

            else

            {

                st[pow][left] = max(st[pow - 1][left], st[pow - 1][min(left + len / 2,n)]);

            }

        }

    }

``````

## 使用示范:

``````

void solve()

{

    int n, m;

    cin >> n >> m;

    vector<int>arr(n+1);

    for (int i = 1; i <= n; i++)

    {

        cin >> arr[i];

    }

    //ST表

    vector<vector<int>>st(30,vector<int>(n+1));//len = 2的i次方

    int len = 1;

    for (int pow = 0; len <= n; pow++, len *=2)

    {

        for (int left = 1; left <= n- len+1; left++)

        {

            if (pow == 0)

                st[0][left] = arr[left];

            else

            {

                st[pow][left] = max(st[pow - 1][left], st[pow - 1][min(left + len / 2,n)]);

            }

        }

    }

    for (int i = 1; i <= m; i++)

    {

        int l, r;

        cin >> l >> r;

        //l+len-1<=r    len = 2的k次方

        //len <= r-l+1

        //k <= log(r-l+1);

        int k = log2(r - l + 1);

        cout << max(st[k][l], st[k][r-pow(2,k)+1]) << endl;

    }

}

``````

# 10.LCA倍增板子

## 预处理:

``````

void solve()

{

    int n, k;

    cin >> n >> k;

    vector<vector<int>>alist(n + 1);

    for (int i = 1; i <= n - 1; i++)

    {

        int x, y;

        cin >> x >> y;

        alist[x].push_back(y);

        alist[y].push_back(x);

    }

    vector<vector<int>>fa(n + 1, vector<int>(22));

    vector<int>dep(n + 1);

    vector<int>leaf;

    auto dfs = [&](int cur, int pa, auto dfs)->void {

        dep[cur] = dep[pa] + 1;

        if (alist[cur].size() == 1)

        {

            leaf.push_back(cur);

        }

        for (auto x : alist[cur])

        {

            if (x != pa)

            {

                fa[x][0] = cur;

                dfs(x, cur, dfs);

            }

        }

    };

    dfs(1, 0, dfs);

    //倍增求父亲

    for (int p = 1; p < 22; p++)

    {

        for (int i = 1; i <= n; i++)

        {

            fa[i][p] = fa[fa[i][p - 1]][p - 1];

        }

    }

``````

## LCA:

``````

auto LCA = [&](int a, int b)->pair<int, int>

{

    ll ans = 0;

    if (dep[a] < dep[b])swap(a, b);

    while (dep[a] > dep[b])

    {

        int dis = (int)log2(dep[a] - dep[b]);

        a = fa[a][dis];

        ans += pow(2, dis);

    }

    for (int i = log2(dep[a]); i >= 0; i--)

    {

        if (fa[a][i] != fa[b][i])   //向上结果不同才跳

        {

            a = fa[a][i];

            b = fa[b][i];

            ans += pow(2, i) * 2;

        }

    }

    if (a != b)

    {

        a = b = fa[a][0];

        ans += 2;

    }

    return { a ,ans };

};

``````

第一个让a,b深度相同的操作是while进行的,二进制拆分。

## 主函数外版:

``````

inline int LCA(int a,int b)

{

    if (dep[a] < dep[b])swap(a, b);

    while (dep[a] > dep[b])

    {

        int dis = (int)log2(dep[a] - dep[b]);

        a = fa[a][dis];

    }

    if (a == b)return a;

    for (int i = log2(dep[a]); i >= 0; i--)

    {

        if (fa[a][i] != fa[b][i])

        {

            a = fa[a][i];

            b = fa[b][i];

        }

    }

    if (a != b)

    {

        a = b = fa[a][0];

    }

    return a;

}

``````

使用示范:(链式前向星等,POJ 3417 Network 树上差分 LCA 链式前向星 仍超时,加上快读过-CSDN博客)

``````

struct node

{

    int b, next;

}edges[maxn*2];

int head[maxn];

int k = 0;

inline void add(int a, int b)

{

    k++;

    edges[k].b = b;

    edges[k].next = head[a];

    head[a] = k;

}

int fa[maxn][23];

int dep[maxn];

void dfs1(int cur ,int pa)

{

    dep[cur] = dep[pa] + 1;

    fa[cur][0] = pa;

    for (int p = 1; p < 22; p++)

        fa[cur][p] = fa[fa[cur][p - 1]][p - 1];

    for (int i = head[cur]; i > 0; i = edges[i].next)

    {

        if(edges[i].b != pa)

            dfs1(edges[i].b, cur);

    }

}

inline int LCA(int a,int b)

{

    if (dep[a] < dep[b])swap(a, b);

    while (dep[a] > dep[b])

    {

        int dis = (int)log2(dep[a] - dep[b]);

        a = fa[a][dis];

    }

    if (a == b)return a;

    for (int i = log2(dep[a]); i >= 0; i--)

    {

        if (fa[a][i] != fa[b][i])

        {

            a = fa[a][i];

            b = fa[b][i];

        }

    }

    if (a != b)

    {

        a = b = fa[a][0];

    }

    return a;

}

int diff[maxn];

int pass[maxn];

int cnt0, cnt1;

void dfs2(int cur,int pa)

{

    pass[cur] += diff[cur];

    for (int i = head[cur]; i > 0; i = edges[i].next)

    {

        if (edges[i].b != pa)

        {

            dfs2(edges[i].b, cur);

            pass[cur] += pass[edges[i].b];

        }

    }

}

``````

# 11.KMP板子 前缀跳后缀 ;前缀函数

## 原理:

当选择的前缀和后缀比较出现不同时,本应结束了,

然而如果匹配串已匹配部分有相同的前缀和后缀,匹配串可以整体向后跳,接着比。

![请添加图片描述](https://img-blog.csdnimg.cn/direct/85eda0cf3adb4d37bacf963862bb6aa2.png)

如图,在原串中找匹配串(模式串),原串中abeab都相同,最后一个位置不相同了。

### nxt数组初始化原理 (前缀函数的优化):

建议阅读:前缀函数与 KMP 算法 - OI Wiki

我这里概括并通俗的解释一下:

前缀函数指的是每个位置为末尾的前缀子串中 ,该子串前缀和后缀 最大相同长度。

(如上图,上面箭头指到我们当前要比较位置且出现不相同,此时匹配串前移一部分接着比即可)

nxt数组几乎就是前缀函数,但是nxt是用来往前跳的(不相同时,模式串下一个要比的位置),而且是跳到相同前缀的下一个不同的位置,用来后续接着比较。

对于构造前缀函数,朴素做法就是蛮力法,每个位置从大到小挨个试。n个位置,每个位置n次,每个比较长度是n,所以是O(n^3)的复杂度。

## OI Wiki的优化1:

![alt text](image-1.png)

此图表示 i 位置最大相同前缀长度是3了,那么i+1位置,最长也是4。

所以上界可以改为4。

我初看以为是常数优化,然而不是的。

如果前面前缀函数值小,后面遍历的次数也少了。

如果前面大,后面就算遍历多,再下一次会很小。

最糟情况的是每次都只比一次就成功,一直攒(只能一个一个攒),最后一次再遍历,所以最多比较次数就是OI Wiki说的 n-1 + n-2 == 2n-3 。 每次比是 n ,所以复杂度为O(n^2)

## 优化2:

这个优化才是重头戏

配好一次后,下一个位置可以直接接着比,如果可以直接得到 +1 的相同前缀长度。

a.如果不同:

![alt text](image-2.png)

b.先不考虑怎么找 j,而是看下这个规律。

![alt text](image-3.png)

c.而这,就是此前 pai [  pai [i] - 1 ]  已经求出的结果

![alt text](image-4.png)

d.应用到KMP是这样的:

![alt text](image-5.png)

(当然如上文提及,仍要记着这里每个位置求的是最长前缀长度。而KMP的nxt数组存的是前跃后下一个比较的位置。)

算法时间复杂度就是O(n)了。

可以想后缀跳前缀,几次就能跳完。

极端情况全是aaaaaaa,最后有一个b。

那么也只有b这里需要前跃n次。而前面一次也不用前跃。

## nxt数组初始化的实现:

1.首先要初始化KMP数组,或着说是nxt数组。这个是模式串用来向前跳的。

(这里初始化和后面匹配的方法大同小异,比较策略都是原串 下标 i 从1 到 m ,一次只比一个位置,模式串位置 j 不合适时就一直往前跳。 不为什么 )

初始化过程:

![alt text](image-6.png)

每个位置都及时更新nxt数组的值,值就是模式串下标。显而易见,上图中相同部分就是记录的相同前后缀的长度。

```

//本题s1是原串,s2是模式串,m,n分别是其对应长度

//本板子字符串下标从1开始,0位置作为出口,用处如下:

void init_next()

{

    int j = 0;

    for (int i = 2; i < n; i++)

    {

        while (j&&s2[i] != s2[j+1])//不相同就前跃,直到相同,或者全不同 j==0

            j = nxt[j];

        if (s2[i] == s2[j + 1])j++;//相同就接着往后比

        nxt[i] = j;//可更新

    }

}

```

## 参考代码:

``````

#include<bits/stdc++.h>

using namespace std;

const int maxn = 1e6;

string s1, s2;

int m, n;

int nxt[maxn];

void init_next()

{

    int j = 0;

    for (int i = 2; i < n; i++)

    {

        while (j&&s2[i] != s2[j+1])

            j = nxt[j];

        if (s2[i] == s2[j + 1])j++;

        nxt[i] = j;

    }

}

vector<int>ret;

void kmp()

{

    int j = 0;

    for (int i = 1; i < m; i++)

    {

        while (j && s1[i] != s2[j + 1])

            j = nxt[j];

        if (s1[i] == s2[j + 1])j++;

        if (j == n - 1)

        {

            ret.emplace_back(i - (n - 2));

            j = nxt[j];

        }

    }

}

int main()

{

    cin >> s1 >> s2;

    s1 = ' ' + s1;s2 = ' ' + s2;

    m = s1.size(), n = s2.size();

    init_next();

    kmp();

    for (int x : ret)

        cout << x << endl;

    for (int i = 1; i < s2.size(); i++)

        cout << nxt[i] << " ";

    return 0;

}

``````

## 参考代码2: string从下标0开始的,nxt[0]为-1的写法:

```

#include<bits/stdc++.h>

using namespace std;

const int maxn = 2e7+5;

//可以像kmp那样,顺便更新其他位置

string s1, s2;

int nxt[maxn];

int m, n;

void init_nxt()

{

    nxt[0] = -1;

    int j = 0;

    for (int i = 1; i < n; i++)

    {

        while (j!=-1&&s2[i] != s2[j])j = nxt[j];

        nxt[i+1] = j+1;//相等所以可以调到这个位置

        if (j == -1 || s2[i] == s2[j])j++;

    }

}

vector<int>ret;

void kmp()

{

    int j = 0;

    for (int i = 0; i < m; i++)

    {

        while (j != -1 && s1[i] != s2[j])j = nxt[j];

        if(j == -1 || s1[i] == s2[j])j++;

        if (j == n)

        {

            ret.emplace_back(i-n+2);

            j = nxt[j];

        }

    }

}

int main()

{

    cin >> s1 >> s2;

    m = s1.size(), n = s2.size();

    init_nxt();

    kmp();

    for (auto x : ret)

        cout << x << endl;

    for (int i = 1; i <= n; i++)

        cout << nxt[i] << " ";

    return 0;

}

```

# 11.5.扩展 KMP/exKMP(Z 函数);后缀函数

z函数是后缀函数,即从字符串每个位置开始的子串 (这就是后缀) 与 整个字符串 相同的长度。

>KMP与拓展KMP的区别:

>kmp构造使用nxt数组,exkmp使用z数组

>nxt数组存的是与模式串前缀相同的,每个下标前的前缀中的后缀,的最大长度+1,且存到下一个位置,因为要接着比

>z数组存的是与模式串前缀相同的,模式串每个下标起始的后缀中的前缀,的最大长度

简单说下这个Z函数快在哪里:

我们构建z函数,设strlen(str) = n,则我们从 1 构建到 n-1。

如下图,对于4开始的位置,长度有3 (下标4,5,6),所以z[4] = 3

那么对于下一波来说,5 6 和 1 2 是已知相同的。 那么这个时候我们直接用z[1]不就行了。

这是复用。

(例:如果z[1]很长,让我们这里超过了6,那就从6接着比。否则就直接拿答案了。)

![alt text](image-7.png)

## 参考代码:

许多板子让z[0] = 0 ,其实是r = -1就可以避免这个。

```

#include<bits/stdc++.h>

using namespace std;

#define int long long

const int maxn = 2e7 + 5;

string s1, s2;

int z[maxn];

int p[maxn];

int m, n;

//自己和自己匹配

//

//# 0.模式串始终是从头开始的,所以各个位置的前缀一定是相同的

//

//# 1.得到 0 ~ r-l 与 l ~ r 是相同的前缀

//              ^

//# 2.【复用子部分】那么之后的i中, i ~ r ,等同于前面 i-l ~ r-l

//                                                            ^

//# 3.而前面这段是比过的,既然相同,直接用,接着比即可

//

int l, r;

void init_z()

{

    l = r = -1;

    z[0] = n;

    for (int i = 1; i < n; i++)

    {

        if (i <= r)

        {

            //比的是 0 和 i 起始的串

            //可复用,z[i]不用从0开始比

            //l -> 0  

            //i -> i-l

            //我们看 i-l 和 0 最长是多少

            //类似马拉车,复用也不能超r

            //if (z[i - l] < r - i + 1)//小于就能确定后面不相等,等于不行

            //{

            //  z[i] = z[i - l];

            //  continue;

            //}

            //else

            //  z[i] = r - i + 1;

            z[i] = min(r - i + 1, z[i - l]);

        }

        while (i + z[i] < n && s2[z[i]] == s2[i + z[i]])

            z[i]++;

        if (i + z[i] - 1 > r)//已触及的尾

            l = i, r = i + z[i] - 1;

    }

}

void init_p()

{

    l = r = -1;

    // r - l 的最值是 模式串的长度哦

    //一句话,转移前去看z

    //exKMP

    for (int i = 0; i < m; i++)

    {

        if (i <= r)//这里z不一定是0

            p[i] = min(r - i + 1, z[i - l]);

   

        while (i + p[i] < m && p[i] < n && s2[p[i]] == s1[i + p[i]])

            p[i]++;

        if (i + p[i] - 1 > r)

            l = i, r = i + p[i] - 1;

    }

}

signed main()

{

    cin >> s1 >> s2;

    m = s1.size(), n = s2.size();

    init_z();

    init_p();

    int ans1 = 0, ans2 = 0;

    for (int i = 0; i < n; i++)

    {

        ans1 ^= (i + 1) * (z[i] + 1);

    }

    for (int i = 0; i < m; i++)

    {

        ans2 ^= (i + 1) * (p[i] + 1);

    }

    cout << ans1 << endl;

    cout << ans2 << endl;

    return 0;

}

```

# 12.扫描线板子 小思路

## 前言:

本板子是结合我的线段树1板子和OIWIKI的扫描线写成的类。

线段树1板子 区间加-CSDN博客

扫描线 - OI Wiki (oi-wiki.org)

## 背景:

照着OI WIKI打了一遍,结果洛谷交上去RE,查了半天查不出来,最后看讨论区,给线段树大小再乘个2,就过了。。

我还数组以为太大了呢

本题数组并不大:

![请添加图片描述](https://img-blog.csdnimg.cn/direct/b7f237f4f6a24a3a9bc629254b57ed38.png)



 

本题的xy是坐标,看的是坐标间的长度,应该是线段树进行二分的时候都要有mid,所以会多分几次。

## 扫描线原理:

我们从下往上扫吧。

1.

离散化就是把出现的x坐标放到数组里,排好序,数组里只有我们要的x。

离散化x后对这个数组进行建树。

2.

然后我们从下往上是根据y坐标进行的,所以每个y捆绑对应的x1,x2,以及加或减。

//这就是整个策略了。

## 代码:

``````

#define ll long long

#define endl "\n"

#define int long long

const ll inf = 1e9;

template<class T>

class ST//segment tree

{

    struct node

    {

        T l, r, sum;

        T t;//懒标记//服务后代

        node() :l(0), r(0), sum(0), t(0)

        {}

    };

    ll n;

    vector<T>a;

    vector<node>d;

    //扫描线中,上面的node中的l,r代表矩形左右

    //下面的l,r是线段树的下标,每个a表示的是一个横坐标

public:

    void push_up(ll i)

    {

        if (d[i].t > 0)//扫描到了

            d[i].sum = d[i].r - d[i].l;

        else

            d[i].sum = d[i * 2].sum + d[i * 2 + 1].sum;

    }

    //直到最左和最右范围,但是不知道其中的和

    void build_tree(ll i, ll l, ll r)

    {

        ll mid = l + (r - l) / 2;

        if (l + 1 < r)

        {

            d[i].l = a[l];

            d[i].r = a[r];

            build_tree(i * 2, l, mid);

            build_tree(i * 2 + 1, mid, r);

            push_up(i);

        }

        else

        {

            d[i].l = a[l];

            d[i].r = a[r];

            d[i].sum = 0;

        }

    }

    void update(ll l, ll r, ll val)

    {

        _update(1, l, r, val);//加并挂标记

    }

    void _update(ll i, ll l, ll r, ll val)

    {

        if (d[i].l == l && d[i].r == r)

        {

            d[i].t += val;

            push_up(i);

            return;

        }

        else

        {

            //中分

            if (d[i * 2].r > l)

                _update(i * 2, l, min(d[i * 2].r, r), val);//r在下一个内就不多传了

            if (d[i * 2 + 1].l < r)

                _update(i * 2 + 1, max(l, d[i * 2 + 1].l), r, val);

            push_up(i);

        }

    }

    T get_sum()

    {

        return d[1].sum;

    }

    ST(vector<T>arr)

    {

        a = arr;

        n = a.size() - 1;

        d = vector<node>(2*pow(2, (ll)log2(n) + 1 + 1) + 1);

        build_tree(1, 1, n);

    }

};

struct scanline

{

    int l, r, h;

    int mark;

    bool operator <(const scanline b)const

    {

        return h < b.h;

    }

};

//总的sum统计了此刻矩形的长度,update一直在更新这个长度

void solve()

{

    int n; cin >> n;

    vector<int>xaxis;

    vector<scanline>yaxis;

    //x离散化用来服务线段树

    //y该多少个就多少个,遇到就改,离散化了求得高度差,同层是0

    //该加加,该减减,区间求和改成求长度吧

    xaxis.push_back(0);

    yaxis.push_back({});

    for (int i = 1; i <= n; i++)

    {

        int x1, y1, x2, y2;

        cin >> x1 >> y1 >> x2 >> y2;

        xaxis.push_back(x1);

        xaxis.push_back(x2);

        yaxis.push_back({ x1,x2,y1,1 });

        yaxis.push_back({ x1,x2,y2,-1 });

    }

    sort(xaxis.begin() + 1, xaxis.end());

    sort(yaxis.begin() + 1, yaxis.end());

    unique(xaxis.begin() + 1, xaxis.end());

    ST<int> demo(xaxis);

    int ans = 0;

    for (int i = 1; i < yaxis.size() - 1; i++)//最后一次不用算了

    {

        demo.update(yaxis[i].l, yaxis[i].r, yaxis[i].mark);

        ans += demo.get_sum() * (yaxis[i + 1].h - yaxis[i].h);

    }

    cout << ans;

}

signed main()

{

    ios::sync_with_stdio(false);

    cin.tie(0);

    cout.tie(0);

    int t = 1;

    //cin >> t;

    while (t--)

    {

        solve();

    }

    return 0;

}

``````

# 13.龟速乘板子 a*二进制拆分的正数b(mod p)

如果要乘负数b,先乘正的,最后结果加负号即可。

``````

ll mul(ll a, ll b, ll p) {

    // 将乘法变为加法,二进制优化,边加边模

    ll ans = 0;

    while (b) {

        if (b & 1)

            ans = (ans + a) % p;

        a = (a + a) % p;

        b >>= 1;

    }

    return ans;

}

``````

# 14.拓展欧几里得法求逆元

## 板子:

x即为最终答案,x可能为负数,加模数即可

乘法逆元 - OI Wiki (oi-wiki.org)

``````

void exgcd(int a, int b, int& x, int& y) {

    if (b == 0) {

        x = 1, y = 0;

        return;

    }

    exgcd(b, a % b, y, x);

    y -= a / b * x;

}

``````

## 使用:

``````

    exgcd(a, n + 1, x, y);//x就是逆元

    while (x <= 0)x += n + 1;

``````

## 原理:

最大公约数 - OI Wiki (oi-wiki.org)

![请添加图片描述](https://img-blog.csdnimg.cn/direct/cd5da2b7eb314a538e70c3d4418c2267.png)



 

## 拓展欧几里得算法:

``````

int Exgcd(int a, int b, int &x, int &y) {

  if (!b) {

    x = 1;

    y = 0;

    return a;

  }

  int d = Exgcd(b, a % b, x, y);

  int t = x;

  x = y;

  y = t - (a / b) * y;

  return d;

}

``````

# 15.二分图板子 匈牙利算法 KM算法

KM算法

## 原理:

匈牙利算法:二分图最大权匹配 - OI Wiki

简单说就是挨个找,找到就退出。后面的来了就让前面的挪位置。

## 板子:

book指给u找位置时,有人考虑过的位置就不考虑了。

match[ i ]就是i位置对应的人。

e是关系

``````

int book[10001];

int match[10001];

bool e[101][101];

int ans=0,n=0,m=0;

bool dfs(int u)

{

    for(int i=1;i<=n;i++)

    {

        if(book[i]==0 && e[u][i]==true)

        {

            book[i]=1;

            if(match[i]==0 || dfs(match[i])==true)

            {

                match[u]=i;

                match[i]=u;

                return true;

            }

        }

    }

    return false;

}

``````

## 使用:

``````

int main()

{

    scanf("%d %d",&n,&m);

    for(int i=1;i<=m;i++)

    {

        int x=0,y=0;

        scanf("%d %d",&x,&y);

        e[x][y]=true;

        e[y][x]=true;

    }

    for(int i=1;i<=n;i++)

    {

        for(int j=1;j<=n;j++)

        {

            book[j]=0;

        }

        if(dfs(i)==true)

        {

            ans++;

        }

    }

    printf("%d",ans);

    return 0;

}

``````

# 16.组合数板子 卢卡斯定理

## 1.直接求组合数:

组合数C(n,m),n个里面选m个,结果为$$ \frac{n! / (n-m)!}{m!}     $$(前者其实就是n* n-1*...*n-m+1,分子分母都是m个数相乘)



 

ksm快速幂求的是逆元。用的是费马小定理,适用于模数为素数的时候。

[快速幂板子](https://blog.csdn.net/JK01WYX/article/details/135589317?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171809405516800211521250%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=171809405516800211521250&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-135589317-null-null.nonecase&utm_term=%E5%BF%AB%E9%80%9F%E5%B9%82&spm=1018.2226.3001.4450)


 

(板子中a是阶乘数组,预处理一下)

```

a[0] = 1;

for(int i=1;i<=n+k;i++)

    a[i] = i*a[i-1]%mod;

```

```

ll ksm(int x, int y, int mod)

{

    if (x == 1) return 1;

    ll res = 1, base = x;

    while (y) {

        if (y & 1) res = (res * base) % mod;

        base = (base * base) % mod;

        y >>= 1;

    }

    return res;

}

ll C(ll n, ll m, ll p)

{

    if (m > n)return 0;

    return ((a[n] * ksm(a[m], p - 2, p)) % p * ksm(a[n - m], p - 2, p) % p);

}

ll A(ll n, ll m, ll p)

{

    if (m > n)return 0;

    return (a[n] * ksm(a[n - m], p - 2, p)) % p;

}

```

## 2.递推组合数公式:

$$C(n, m) = C(n - 1, m) + C(n - 1, m - 1)$$

我们拿出一个元素,剩下n-1个。要么在 n-1 里面选 m 个,要么这个加上 n-1 里面选 m-1 个

```

private static final int MX = 31;

private static final int[][] c = new int [MX][MX];

static {

    c[0][0] = c[1][0] = 1;

    for(int i=1;i<MX;i++)

    {

        c[i][0] = 1;

        for(int j=1;j<=i;j++)

        {

            c[i][j] = c[i-1][j] + c[i-1][j-1];

        }

    }

}

```

```

// 1 1

// 2 1 , 2 2

// 3 1 , 3 2 , 3 3

// 4 1 , 4 2 , 4 3 , 4 4

```

## 3.杨辉三角

上面这个递推结果正是杨辉三角。

```

// 1

// 1 1

// 1 2 1    

// 1 3 3 1                 C(3,0) C(3,1) C(3,2)

// 1 4 6 4 1               C(4,0) C(4,1) C(4,2)

```

---

## 4.Lucas定理

>Lucas 定理用于求解大组合数取模的问题,其中模数必须为素数。正常的组合数运算可以通过递推公式求解(详见 排列组合),但当问题规模很大,而模数是一个不大的质数的时候,就不能简单地通过递推求解来得到答案,需要用到 Lucas 定理。

a是阶乘数组,提前处理好,处理到模数应该够的。

ksm快速幂

C是组合数函数,ksm是用来费马小定理求逆元(即倒数)。就是组合数公式,n的阶乘除以(m的阶乘和n-m的阶乘)。

Lucas 卢卡斯定理 - OI Wiki (oi-wiki.org)

``````

ll a[100005];

ll ksm(int x, int y,int mod) {//因为数据范围很大容易爆掉,所以就要Fast_Pow

    if (x == 1) return 1;

    ll res = 1, base = x;

    while (y) {

        if (y & 1) res = (res * base) % mod;

        base = (base * base) % mod;

        y >>= 1;

    }

    return res;

}

ll C(ll n, ll m,ll p)

{

    if (m > n)return 0;

    return ((a[n] * ksm(a[m], p - 2, p)) % p * ksm(a[n - m], p - 2, p) % p);

}

long long Lucas(long long n, long long m, long long p)

{

    if (m == 0) return 1;

    return (C(n % p, m % p, p) * Lucas(n / p, m / p, p)) % p;

}

``````

# 17.高精度加法,乘法板子

## High precision addition

``````

string hpa(string str1,string str2)

{

    int a[10000] = { 0 }, b[10000] = { 0 }, c[10000] = { 0 };

    int len1 = str1.size(), len2 = str2.size();

    //`len1>=len2

    if (len2 > len1)swap(str1, str2), swap(len1, len2);

    for (int i = 0; i < len1; i++)

        a[i] = str1[len1-1-i] - '0';

    for (int i = 0; i < len2; i++)

        b[i] = str2[len2-1-i] - '0';

    for (int i = 0; i < len1; i++)

    {

        c[i + 1] = (a[i] + b[i] + c[i])/10;

        c[i] = (c[i] + a[i] + b[i]) % 10;

    }

    if (c[len1] != 0)len1++;

    string ret;

    for (int i = len1 - 1; i >= 0; i--)

    {

        ret += '0' + c[i];

    }

    return ret;

}

``````

## High precision multiplication

``````

//     999

//     999

//----------

//    8991

//   8991

//  8991

//  123456

string hpm(string str1,string str2)

{

    int a[10000] = { 0 }, b[10000] = { 0 }, c[10000] = { 0 };

    int len1 = str1.size(), len2 = str2.size();

    for (int i = 0; i < len1; i++)

        a[i] = str1[len1 - 1 - i] - '0';

    for (int i = 0; i < len2; i++)

        b[i] = str2[len2 - 1 - i] - '0';

    for (int i = 0; i < len1; i++)

    {

        for (int j = 0; j < len2; j++)

        {

            c[i + j] += a[i] * b[j];//也可以顺便在最后处理

        }

    }

    int len = len1 + len2;

    for (int i = 0; i < len; i++)

    {

        c[i + 1] += c[i] / 10;

        c[i] = c[i] % 10;

    }

    while (c[len - 1] == 0&&len>1/**/)

    {

        len--;

    }

    string ret;

    for (int i = len - 1; i >= 0; i--)

    {

        ret += '0' + c[i];

    }

    return ret;

}

``````

# 18.排列组合板子 A C

a是阶乘数组,预处理一下

ksm快速幂求的是逆元。用的是费马小定理,适用于模数为素数的时候。

``````

ll ksm(int x, int y, int mod)

{

    if (x == 1) return 1;

    ll res = 1, base = x;

    while (y) {

        if (y & 1) res = (res * base) % mod;

        base = (base * base) % mod;

        y >>= 1;

    }

    return res;

}

ll C(ll n, ll m, ll p)

{

    if (m > n)return 0;

    return ((a[n] * ksm(a[m], p - 2, p)) % p * ksm(a[n - m], p - 2, p) % p);

}

ll A(ll n, ll m, ll p)

{

    if (m > n)return 0;

    return (a[n] * ksm(a[n - m], p - 2, p)) % p;

}

``````

# 19.快读快写板子

``````

inline int read()

{

    int x = 0, f = 1;

    char ch = getchar();

    while (ch < '0' || ch>'9')

    {

        if (ch == '-')

            f = -1;

        ch = getchar();

    }

    while (ch >= '0' && ch <= '9')

    {

        x = (x << 1) + (x << 3) + (ch ^ 48);

        ch = getchar();

    }

    return x * f;

}

inline void write(int x)

{

    if (x < 0)

    {

        putchar('-');

        x = -x;

    }

    if (x > 9)

        write(x / 10);

    putchar(x % 10 + '0');

}

``````

# 20.分解质因数板子

``````

vector<int>divide(int x)

{

    vector<int>ret;

    for (int i = 2; i <= x / i; i++)//i*i <= x

    {

        if (x % i == 0)

        {

            ret.push_back(i);

            while (x % i == 0)

                x /= i;

        }

    }

    if (x > 1)ret.push_back(x);

    return ret;

}

``````

# 21.快速选择 数组中的第K个最大元素

``````

class Solution {

    int _k;

public:

    // [0,l][l+1,r-1][r,nums.size()-1]

    int _sort(int left,int right,vector<int>& nums)

    {

        if(left==right)return nums[left];

        int aim = getRandom(left,right,nums);

        int i = left,l = left-1,r = right+1;

        while(i<r)

        {

            if(nums[i]<aim)swap(nums[++l],nums[i++]);

            else if(nums[i] == aim)i++;

            else swap(nums[--r],nums[i]);

        }

        if(nums.size()-1-r+1>=_k)

            return _sort(r,right,nums);

        else if(nums.size()-1-(l+1)+1>=_k)

            return nums[i-1];

        else

            return _sort(left,l,nums);

    }

    int getRandom(int left,int right,vector<int>& nums)

    {

        int r = rand();

        return nums[r%(right-left+1) + left];

                    /*    偏移量   */

    }

    int findKthLargest(vector<int>& nums, int k)

    {

        srand(time(NULL));

        _k = k;

        return _sort(0,nums.size()-1,nums);

    }

};

``````

# 22.打表26个质数

``````

int arr[26] = {2,3,5,7,11

    ,13,17,19,23,29

    ,31,37,41,43,47

    ,53,59,61,67,71

    ,73,79,83,89,97,101};

``````

# 23.多重背包问题

``````

const int MAX = 1e3 + 5;

int v[MAX], w[MAX], s[MAX];

int main()

{

    ios::sync_with_stdio(false);

    cin.tie(0);

    cout.tie(0);

    int N, V;

    cin >> N >> V;

    for (int i = 1; i <= N; i++)

    {

        cin >> w[i] >> v[i] >> s[i];

    }

    int dp[MAX] = { 0 };

    for (int i = 1; i <= N; i++)

    {

        if (s[i] == -1)

        {

            for (int j = V; j >= w[i]; j--)

            {

                dp[j] = max(dp[j], dp[j - w[i]]+v[i]);

            }

        }

        else if (s[i] == 0)

        {

            for (int j = w[i]; j <= V; j++)

            {

                dp[j] = max(dp[j], dp[j - w[i]] + v[i]);

            }

        }

        else if (s[i] > 0)

        {

            int num = min(s[i], V/w[i]);

            for (int k = 1;num>0; k <<= 1)

            {

                if (num < k)k = num;

                num -= k;

                for (int j = V; j >= w[i] * k; j--)

                {

                    dp[j] = max(dp[j], dp[j-w[i]*k]+v[i]*k);

                }

            }

        }

    }

    cout << dp[V];

    return 0;

}

``````

# 24.分治

## 归并

``````

class Solution {

    int marr[50005];

public:

    void merge(int left,int right,vector<int>&arr)

    {

        if (left >= right)return;

        int mid = (left + right)>>1;//3 /2  + 5/2

        merge(left, mid,arr);

        merge(mid+1, right,arr);

        int aim = left;

        int part1 = left, part2 = mid + 1;

        while (part1<=mid && part2<=right)

        {

            if (arr[part1] <= arr[part2])

                marr[aim++] = arr[part1++];

            else

                marr[aim++] = arr[part2++];

        }

        while (part1 <= mid)

            marr[aim++] = arr[part1++];

        while (part2 <= right)

            marr[aim++] = arr[part2++];

        //最后再赋回去。。

        for (int i = left; i <= right; i++)

            arr[i] = marr[i];

    }

    vector<int> sortArray(vector<int>& nums)

    {

        merge(0,nums.size()-1,nums);

        return nums;

    }

};

``````

## 数组中第k小元素 ; cpp库函数

![alt text](image-8.png)

```

class Solution {

    int _k;

public:

    // [0,l][l+1,r-1][r,nums.size()-1]

    int _sort(int left,int right,vector<int>& nums)

    {

        if(left==right)return nums[left];

        int aim = getRandom(left,right,nums);

        int i = left,l = left-1,r = right+1;

        while(i<r)

        {

            if(nums[i]<aim)swap(nums[++l],nums[i++]);

            else if(nums[i] == aim)i++;

            else swap(nums[--r],nums[i]);

        }

        if(nums.size()-1-r+1>=_k)

            return _sort(r,right,nums);

        else if(nums.size()-1-(l+1)+1>=_k)

            return nums[i-1];

        else

            return _sort(left,l,nums);

    }

    int getRandom(int left,int right,vector<int>& nums)

    {

        int r = rand();

        return nums[r%(right-left+1) + left];

                    /*    偏移量   */

    }

    int findKthLargest(vector<int>& nums, int k)

    {

        srand(time(NULL));

        _k = k;

        return _sort(0,nums.size()-1,nums);

    }

};

作者:昕水

链接:https://leetcode.cn/problems/kth-largest-element-in-an-array/solutions/2807885/cppku-han-shu-shu-zu-zhong-di-kxiao-yuan-1ne4/

来源:力扣(LeetCode)

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

```

# 25.判断是素数质数

```

bool is_prime(int x)

{

    for(int i = 2; i * i <= x; i++)

    {

        if (x % i == 0)return false;

    }

    return true;

}

```

# 26.素数筛法

## Eratosthenes 筛法(埃拉托斯特尼筛法,简称埃氏筛法)筛至平方根

考虑这样一件事情:对于任意一个大于 1 的正整数 n,那么它的 x 倍就是合数(x > 1)。利用这个结论,我们可以避免很多次不必要的检测。

如果我们从小到大考虑每个数,然后同时把当前这个数的所有(比自己大的)倍数记为合数,那么运行结束的时候没有被标记的数就是素数了。

```

vector<int> prime;

bool is_prime[N];

void Eratosthenes(int n) {

  is_prime[0] = is_prime[1] = false;

  for (int i = 2; i <= n; ++i) is_prime[i] = true;

  // i * i <= n 说明 i <= sqrt(n)

  for (int i = 2; i * i <= n; ++i) {

    if (is_prime[i])

      for (int j = i * i; j <= n; j += i) is_prime[j] = false;

  }

  for (int i = 2; i <= n; ++i)

    if (is_prime[i]) prime.push_back(i);

}

```

## 分块筛选

```

int count_primes(int n) {

  const int S = 10000;

  vector<int> primes;

  int nsqrt = sqrt(n);

  vector<char> is_prime(nsqrt + 1, true);

  for (int i = 2; i <= nsqrt; i++) {

    if (is_prime[i]) {

      primes.push_back(i);

      for (int j = i * i; j <= nsqrt; j += i) is_prime[j] = false;

    }

  }

  int result = 0;

  vector<char> block(S);

  for (int k = 0; k * S <= n; k++) {

    fill(block.begin(), block.end(), true);

    int start = k * S;

    for (int p : primes) {

      int start_idx = (start + p - 1) / p;

      int j = max(start_idx, p) * p - start;

      for (; j < S; j += p) block[j] = false;

    }

    if (k == 0) block[0] = block[1] = false;

    for (int i = 0; i < S && start + i <= n; i++) {

      if (block[i]) result++;

    }

  }

  return result;

}

```

## 线性筛法

也称为 Euler 筛法(欧拉筛法)

```

vector<int> pri;

bool not_prime[N];

void pre(int n) {

  for (int i = 2; i <= n; ++i) {

    if (!not_prime[i]) {

      pri.push_back(i);

    }

    for (int pri_j : pri) {

      if (i * pri_j > n) break;

      not_prime[i * pri_j] = true;

      if (i % pri_j == 0) {

        // i % pri_j == 0

        // 换言之,i 之前被 pri_j 筛过了

        // 由于 pri 里面质数是从小到大的,所以 i 乘上其他的质数的结果一定会被

        // pri_j 的倍数筛掉,就不需要在这里先筛一次,所以这里直接 break

        // 掉就好了

        break;

      }

    }

  }

}

```

# 27.bitset

使用示范:

1.用 <> {} 来初始化

2。test看从右到左第几个位置的值

示例1:

```

// bitset::operator[]

#include <iostream>       // std::cout

#include <bitset>         // std::bitset

int main ()

{

  std::bitset<4> foo;

  foo[1]=1;             // 0010

  foo[2]=foo[1];        // 0110

  std::cout << "foo: " << foo << '\n';

  return 0;

}

```

示例2:

```

class Solution {

public:

    int maxTotalReward(vector<int>& rewardValues) {

        ranges::sort(rewardValues);

        rewardValues.erase(unique(rewardValues.begin(), rewardValues.end()), rewardValues.end());

        bitset<100000> f{1};

        for (int v : rewardValues) {

            int shift = f.size() - v;

            // 左移 shift 再右移 shift,把所有 >= v 的比特位置 0

            // f |= f << shift >> shift << v;

            f |= f << shift >> (shift - v); // 简化上式

        }

        for (int i = rewardValues.back() * 2 - 1; ; i--) {

            if (f.test(i)) {

                return i;

            }

        }

    }

};

```

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
静电是指电荷的积聚,当电荷在板子上积累过多时,会导致电场强度超过特定电压,触发板子的重启机制。这种现象在USB接口中更容易发生,因为USB接口在插拔过程中,电流会快速地变化,容易产生静电。造成静电的原因主要有以下几个方面: 1. 按键误操作:在连接或断开USB设备时,如果没有正确地按下电源按钮或选择正常的操作流程,可能会导致静电产生。 2. 环境问题:在干燥的环境中,静电更容易积聚。尤其是在冬季,由于空气湿度较低,有利于静电的形成和积累。 3. 材料摩擦:当不同材料摩擦时,摩擦产生的电荷会沿着导体表面分布,当接触板子时,会导致板子上的电荷积累。 为了解决这个问题,我们可以采取以下一些措施: 1. 接地处理:保证机箱和板子都接地,这样可以有效地将多余的静电洩放到地面。 2. 静电防护措施:使用防静电手腕带或穿防静电服等工具来减少静电的产生和传导。 3. 调整环境:保持环境湿度在适当范围内,避免静电的产生。 4. 规范操作:正确操作USB接口,按照设备的要求进行插拔操作,避免不必要的误操作。 5. 使用带有防静电保护的USB设备:选择具备防静电设计的USB设备,可以减少静电对板子的影响。 总之,由于静电在USB接口上积累,容易导致板子的重启。通过采取适当的措施和改变操作习惯,可以减少静电对板子的影响,提高电子设备的稳定性和可靠性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值