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 重大修改

相關文章
相關標籤/搜索