OI 基础模板

考试必备

1.快速读入

inline ll read()
{
    int x=0,f=0;
    char ch=getchar();
    while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return f?-x:x;
}

2.对拍

3.卡常小技巧

① 位运算乘除html

\[x=x\times2^n ~能够转化成~ x<<n \]

\[x=x÷2^n ~能够转化成~ x>>n \]

② for 卡常node

for(register int i(1); i<=n; ++i) {}

③ 短函数前加 inline 卡常git

④ 判断奇偶数组

if(a%2==0)  能够转化成  if((a&1)==0)

⑤ 取模用 &数据结构

x=667%4;    能够转化成  x=667&(4-1);
x=667%32;   能够转化成  x=667&(32-1);

⑥ 正负转换用位运算函数

i=-1;       能够转换成  i=~i+1;   或   i=(i^-1)+1;

⑦ 取绝对值spa

k=abs(x);   能够转换成  k=(x^(x>>31))-(x >> 31);

数据结构

1.ST表

静态查询区间最值。code

ll f[100001][20];
ll n,m,a[100001];

void ST_make(){
    for(int i=1;i<=n;++i) f[i][0]=a[i];
    ll t=log(n)/log(2)+1;
    for(int j=1;j<t;++j)
        for(int i=1;i<=n-(1<<j)+1;++i)
            f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}

ll ST_q(ll l,ll r)
{
    ll k=log(r-l+1)/log(2);
    return max(f[l][k],f[r-(1<<k)+1][k]);
}

int main()
{
    n=read();
    m=read();
    for(int i=1;i<=n;++i)
    a[i]=read();
    ST_make();
    for(int i=1;i<=m;++i)
    {
        ll l=read(),r=read();
        printf("%lld\n",ST_q(l,r));
    }
    return 0;
}

数学

1.线性筛素数

给定一个整数 \(n\) ,求出 $[2,n] $ 之间的全部素数。htm

思路:prime 数组存放已经筛出的素数, \(m\) 表明素数个数(也就是说遍历时从 \(1\) 遍历到 \(m\) 便可),v 数组表明有没有被标记,避免重复筛。blog

int v[maxn],prime[maxn],n,k,t,m;
void primes(int n)
{
    memset(v,0,sizeof(v));//清空标记数组
    m=0;//质数个数
    for(int i=2;i<=n;i++)
    {
        if(!v[i])//未被标记,i为质数
            v[i]=i,    prime[++m]=i; //记录
        for(int j=1;j<=m;j++)
        {
            if(prime[j]>v[i]||prime[j]>n/i) break;//i有更小的质因子,或者超出n的范围
            v[i*prime[j]]=prime[j];//prime[j]为合数 i*prime[j]的最小质因子
        }
    }
}
int main()
{
    scanf("%d",&n);
    primes(n);
    for(int i=1;i<=m;++i)
        printf("%d\n",prime[i]);
}

2.最大公约数 \((gcd)\)

① 标准

inline int gcd(int a,int b) {
    int r;
    while(b>0) 
    {
        r=a%b;
        a=b;
        b=r;
    }
    return a;
}

② 三目

inline int gcd(int a,int b) 
{
    return b>0 ? gcd(b,a%b):a;
}

③ 位运算

inline int gcd(int a,int b) //a,b不能为0
{ 
    while(b^=a^=b^=a%=b);
    return a;
}

④ 展转相除法

inline int gcd(int a,int b) 
{
    if(a%b==0) return b;
        else return (gcd(b,a%b));
}

⑤ 展转相减法

int gcd(int a,int b)
{
    if(a==b)return a;
    return a>b?gcd(a-b,b):gcd(b-a,a);
}

⑥ 外挂(考试禁止)

#include <algorithm>
inline int gcd(int a,int b) 
{
    return __gcd(a,b); //其实能够在主函数里直接用这个
}

分治

1.快速乘法取余

给定三个整数 \(a,n,mod\) ,求 \(a \times n ~\%~mod\) 的值。

inline int mult_mod(int a,int n,int mod)
{
    int ans=0;
    while(n>0)
    {
        if(n&1) ans=(ans+a)%mod;
        a = (a+a)%mod;
        n >>= 1;
    }
    return ans;
}

这个东西好像没有必要的样子,貌似只须要 \((a~\%~mod)×(n~\%~mod)~\%~mod\) 便可。

2.快速幂(非递归)

给定三个整数 \(a,n,mod\) ,求 \(a^n~\%~mod\) 的值。

int ksm(int a,int n,int mod)
{
    if(a==1 || n==0 ) 
    {
        if(mod==1) return 0;
        return 1;
    }
    int m=a,ans=1;
    while(n>0)
    {
        if(n&1)
        {
            ans*=m;    
            ans%=mod;
        }
        m*=m;
        m%=mod;    
        n>>=1;
    }
    return ans;
}

3.最长上升子序列 (LIS)

求一个序列的最长上升子序列个数。
本程序采用边读边处理 + 二分法。

ll f[maxn], ans = 1; //注意答案个数初始化为1

int main()
{
    ll n = read();
    for (int i = 1; i <= n; ++i)
    {
        int x = read();
        if (i == 1)
        {
            f[1] = x;
            continue;
        }
        int l = 1, r = ans, mid;
        while (l <= r)
        {
            mid = (l + r) >> 1;
            if (x <= f[mid])
                r = mid - 1;
            else
                l = mid + 1;
        }
        f[l] = x;
        if (l > ans)
            ++ans;
    }
    printf("%lld\n", ans);
    return 0;
}

4. lower_bound

使用前提:数列为有序数列。

①数组中二分查找

//查找范围:[ begin , end ) ,左闭右开区间。 
*lower_bound(begin,end,num);  //返回第一个 >= num 的数的数值
lower_bound(begin,end,num)-begin; // 返回下标

实际操做:
int a[100]={0,1,3,5,7,9,10};
//    下标:0 1 2 3 4 5  6
int main()
{
    int x=lower_bound(a+1,a+6+1,6)-a; //输出下标
    int y=*lower_bound(a+1,a+6+1,6);  //输出值
    printf("%d %d",x,y);
    return 0;
}

输出结果:4 7

②结构体内二分查找

结构体内使用 lower_bound 须要重载,下面咱们主要对结构体中的 \(a\) 进行操做。

struct node //开结构体
{
    int a, id; //定义结构体内的两个变量
    node() {}
    node(int x, int y) : a(x), id(y) {}
    bool operator < (const node t) const //重载
    {
        return a < t.a;
    }
}t[1001];

bool cmp(node x,node y) //快排 cmp 比较函数
{
    if(x.a < y.a) return 1; //结构体内按 a 由小到大排序。
    return 0;
}

int main()
{
    int n=read(); //数列中数的个数
    for(int i=1;i<=n;++i){
        t[i].a=read(); //读入数列
        t[i].id=i;
    }
    sort(t+1,t+n+1,cmp); //按小到大排序

    int x,xiabiao,ans;
    x=read(); //须要查找的数字
    xiabiao=lower_bound(t+1,t+n+1,node(x,0))-t; //这样求下标
    ans=(*lower_bound(t+1,t+n+1,node(x,0))).a;  //这样求值
    printf("%d %d\n",xiabiao,ans);
    return 0;
}

输入:
5
20 40 30 10 50
35
输出:
4 40

另:upper_bound 的使用与 lower_bound 的使用相似,只不过是严格大于(>)。

图论

前置:链式前向星存图

int head[maxn], dis[maxn];
bool vis[maxn];
struct node
{
    ll nxt, to, w;
}t[maxn];
void add (const int u,const int v,const int w)
{
    t[++tot].to = v;
    t[tot].w = w;
    t[tot].nxt = head[u];
    head[u] = tot;
}

1.Dijkstra 最短路

求单源 \(s\) 到任意一点的最短路径。最短路径保存在数组 dis 中。

#include <queue>
priority_queue <pair <ll, ll> > q;
void dijkstra(int s)
{
    memset (dis, 0x3f3f3f3f, sizeof (dis));      //初始边无限大
    memset (vis, 0, sizeof (vis));        //结点初始均为访问 
    dis[s] = 0;    //起点到本身距离为0 
    q.push (make_pair (0, s));    //起点进队 
    while (q.size() != 0)
    {
        x = q.top().second;
        q.pop();    //初始结点入队  
        if (vis[x])  
            continue;    //若是走过,直接跳过 
        vis[x] = 1;    //标记已访问 
        for (ll i = head[x]; i!=-1; i = t[i].nxt)
        {
            ll y = t[i].to, z = t[i].w;
            if (dis[y] > dis[x] + z)
            {
                dis[y] = dis[x] + z;    //更新起点到y最短路 
                q.push (make_pair (-dis[y], y));    //d[y]相反数入队,转小根堆
            }
        }
    }
}
int main()
    for(int i=1;i<=n;++i)    head[i]=-1;
    //后面省略

2.SPFA 最短路&最长路&判断负环

SPFA能处理负边权,能够判断负环。也能够求最长路。

①最短路

#include<queue>
queue<int> q;
void SPFA(int s)
{
    fill(dis+1,dis+1+n,2147483647);//初始边无限大    
    memset(vis,0,sizeof vis);
    dis[s]=0;
    q.push(s);
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        vis[x]=0;
        for(int i=head[x];i!=-1;i=t[i].nxt)
        {
            int y=t[i].to,z=t[i].w;
            if(dis[y]>dis[x]+z)
            {
                dis[y]=dis[x]+z;
                if(vis[y]==0)
                {
                    q.push(y);
                    vis[y]=1;
                }
            }
        }
    }
}

②最长路

可依据最短路代码进行修改。

1. `dis` 数组赋初值时,若是没有负边权就赋 $-1$ ,若是有负边权就赋无限小。
  1. dis[y]>dis[x]+z 中的 > 改为 <

③判断负环

可在最短路代码基础上进行修改。需新加入一个数组 cnt ,专门记录负环。

补充代码:

ll cnt[maxn];//专门记录负环
void SPFA().....
if(dis[y]>dis[x]+z)
{
    dis[y]=dis[x]+z;
    cnt[y]++;
    if(cnt[y]>=n+1)//出现超过n次表示就有负环 
    {
        ans=1; //ans=1表明有负环。
        return;
    } 
    if(vis[y]==0)
    {
        q.push(y);
        vis[y]=1;
    }
}

3.Floyd 全源最短路

inline void floyd()
{
    for(k=1;k<=n;k++)  
        for(i=1;i<=n;i++)  
            for(j=1;j<=n;j++)  
                if(e[i][j]>e[i][k]+e[k][j])   
                    e[i][j]=e[i][k]+e[k][j];  
}

4.并查集

\(n\) 表明元素个数,\(m\) 为操做数。

\(opt=1\) 时,合并集合 \(a,b\)\(opt=2\) 时,若是 \(a,b\) 在同一集合,输出 Y 不然输出 N

int find(int k)
{
    if(f[k]==k)return k;
    return f[k]=find(f[k]);
}

int main()
{
    n=read();
    m=read();
    for(int i=1;i<=n;++i) f[i]=i; //初始化本身的老大是本身
     
    for(int i=1;i<=m;++i)
    {
        int opt,a,b;
        opt=read();
        a=read();  b=read();
        if(opt==1) 
            f[find(a)]=find(b);
        else
        {
            if(find(a)==find(b))
            printf("Y\n");
            else printf("N\n");
        }
    }
    return 0;
}

5.Prim 最小生成树

int ans,cnt,now=1;//Prim
void prim()
{
    for(int i=2;i<=n;++i)
        dis[i]=MAXN;
        
    for(int i=head[1];i;i=t[i].nxt)
        dis[t[i].to] = min(dis[t[i].to],t[i].w);
    
    while(++cnt<n)
    {
        int minn=MAXN;
        vis[now]=1;
        for(int i=1;i<=n;++i)
        {
            if(vis[i]) continue;
            if(minn > dis[i])
            {
                minn=dis[i];
                now=i;
            }
        } 
        
        ans+=minn;
        
        for(int i=head[now];i;i=t[i].nxt)
        {
            int y=t[i].to, z=t[i].w;
        if(vis[y]) continue;
            if(dis[y] > z)
            {
                dis[y]=z;
            }
        }
    }
}

经常使用 STL 初步

1.队列(先进先出)

#include<queue>
queue<int> q; //priority_queue<int> q(从大到小排序);
q.empty();  //判断队列是否为空
q.size();   //返回队列长度
q.push(item);   //对于queue,在队尾压入一个新元素
                //对于priority_queue,在基于优先级的适当位置插入新元素
q.pop();    //弹出队首元素

//queue only:
q.front();  //返回队首元素的值,但不删除该元素
q.back();   //返回队尾元素的值,但不删除该元素
     
//priority_queue only:
q.top();    //返回具备最高优先级的元素值,但不删除该元素

2.栈(先进后出)

#include<set>
stack<int> s;
stack< int, vector<int> > stk;  //覆盖基础容器类型,使用vector实现stk
s.empty();  //判断stack是否为空,为空返回true,不然返回false
s.size();   //返回stack中元素的个数
s.pop();    //删除栈顶元素,但不返回其值
s.top();    //返回栈顶元素的值,但不删除此元素
s.push(item);   //在栈顶压入新元素item

3.set(自动从小到大排序,自动去重)

set<int> s;//multiset<int> s (不去重)
set<int>::const_iterator iter; //迭代器 

s.insert(); //插入
s.erase();    //删除元素值
s.empty();    //判断set是否为空,为空返回true,不然返回false
s.count();    //返回某个值元素的个数
s.clear();    //清除全部元素
s.begin();    //返回指向第一个元素的迭代器
--s.end();    //返回指向最后一个元素的迭代器
*s.begin();    //返回指向第一个元素的值
*--s.end();    //返回指向最后一个元素的值
               //区间形式为 [ begin , end ) ,因此 end 要自减
s.size();    //返回集合中元素的个数
*s.lower_bound(k);    //返回第一个大于等于k的元素值
*s.upper_bound(k);    //返回第一个大于k的元素值
            //若是没有符合条件的值,则输出 s.size()

/* 遍历 */
for(iter = s.begin() ; iter != s.end() ; ++iter)
{
    cout<<*iter<<" ";//使用迭代器遍历 
}

4.map

#include<map>
map<string,int> m;//string 是 key,int 是 value。
m.size();   //输出元素个数
m.empty();  //若是 map 为空则返回 true
m.clear();  //删除全部元素
......

m["AKIOI"]=10;
cout<<m["AKIOI"];
输出结果:10

· unordered_map

#include<unordered_map>
用法:与 map 差异不大。
优势:由于内部实现了哈希表,所以其查找速度很是的快
缺点:哈希表的创建比较耗费时间
适用处:对于查找问题,unordered_map会更加高效一些,所以遇到查找问题,常会考虑一下用 unordered_map

EdisonBa

2020.11.6 初次编辑

2020.12.1 重大修改

相关文章
相关标签/搜索