倍增筆記♂

聽說前幾天在某博文立了個flag,說要在北京補博客…果然不要亂立flag啊!【此處省去一萬個flag】

  • RMQ問題(Range Minimum/Maximum Query)
    RMQ問題是指對於長度爲n的數列A,回答若干詢問RMQ(A,i,j)(i,j<=n),返回數列A中下標在i,j裏的最小(大)值,也就是說,RMQ問題是指求區間最值的問題。【來自百度百科】
    一般情況下 RMQ問題可以用ST表解決(這裏的一般情況是指沒有修改操作,只進行查詢)首先用dp進行預處理,通過進行合併兩個區間的信息,來得知大的區間的信息。每次可以O(1)查詢。
    我們設f[i][j]爲從i開始連續的2^j次方個數中的最大值。f[i][0]則爲從i開始的2^0次方個數,也就是從i開始1個數的最大值,也就是a[i]。這裏要進行初始化。接下來是狀態轉移方程。我們把這個大區間分爲兩個區間(這個區間的長度肯定爲偶數)。一段爲i到i+2^(j-1)-1,一段爲i+2^(j-1)到i+2^j-1(這兩段的長度都爲2^(j-1))。所以我們就有了這樣的一個狀態轉移方程:f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1])。在查詢的時候,我們設p等於以二爲底r-l+1的對數,把區間分爲兩個區間完全覆蓋。舉例說明,要求區間[2,8]的最大值,就要把它分成[2,5]和[5,8]兩個區間,因爲這兩個區間的最大值我們可以直接由f[2][2]和f[5][2]得到。
    有個圖可以直觀的感受一下:
    這裏寫圖片描述

有個裸題是poj 3264 Balanced Lineup 鏈接

下面是代碼(代碼當中的k就是轉移方程的i,i就是轉移方程的j,j則是方便寫代碼的):

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int MAXN=50000+10;
int f[MAXN][20],g[MAXN][20];
int a[MAXN];
int main()
{
    int n,q;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    {
        f[i][0]=a[i];
        g[i][0]=a[i];
    }
    for(int i=1,j=2;j<=n;i++,j*=2)
    {
        for(int k=1;k+j-1<=n;k++)
        {
            f[k][i]=max(f[k][i-1],f[k+(j>>1)][i-1]);
            g[k][i]=min(g[k][i-1],g[k+(j>>1)][i-1]);
        }
    }
    while(q--)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        int p=int(log(r-l+1)/log(2));
        int maxx=max(f[l][p],f[r-(1<<p)+1][p]);
        int minn=min(g[l][p],g[r-(1<<p)+1][p]);
        printf("%d\n",maxx-minn);
    }
    return 0;
}
  • LCA(Lowest Common Ancestors) 最近公共祖先
    之前只會暴力的LCA,現在終於會了倍增版的。說句實話,打次的倍增不如暴力啊!有圖爲證!

這裏寫圖片描述

【其實是自己太弱了】

其實倍增的LCA就是先對於樹上的每個點用dfs序進行編號。然後在樹上做好st表,但個人認爲這個不如st表好理解QAQ。

我們設father[i][j]表示從i號點2^j倍的祖先,則father[i][0]即爲節點i的父親。
則father[i][j]=father[father[i][j-1]][j-1](從i跳到2^(j-1)倍,再從這個點網上跳2^(j-1)倍)
我最初學倍增的時候總會想會不會跳過去啊…其實後來發現,當它跳到它們的祖先,快要跳到祖先的時候,就會一步一步的跳,也就是father[i][0]。這樣就會保證不會跳出去了。

poj 1330 Nearest Common Ancestors是個裸題【從名字就可以看出來233】鏈接

代碼如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=100000+50;
struct edge{
    int from,to;
}es[MAXN<<1];
int first[MAXN],next[MAXN],deep[MAXN],fa[MAXN];
int tot;
int father[MAXN][30];
void init()
{  
    memset(first,-1,sizeof(first));
    memset(deep,0,sizeof(deep));
    memset(fa,0,sizeof(fa));
    tot=0;
}
void build(int ff,int tt)
{
    es[++tot]=(edge){ff,tt};
    next[tot]=first[ff];
    first[ff]=tot;
}
void dfs(int u, int f)
{
    deep[u]=deep[f]+1;
    fa[u]=f;
    father[u][0]=f;
    for(int i=1;i<=24;i++)
        father[u][i]=father[father[u][i-1]][i-1];
    for(int i=first[u];i!=-1;i=next[i])
    {
        int v=es[i].to;
        if(v==f) continue;
        dfs(v,u);
    }
}
int lca(int x,int y)
{
    if(deep[x]<deep[y])
        swap(x,y);
    if(deep[x]>deep[y])
    {
        int t=deep[x]-deep[y];
        for(int i=24;i>=0;i--)
        {
            if(t&(1<<i))    //如果t的第i位=1 就跳這些步
                x=father[x][i];
        }
    } 
    if(x!=y)
    {
        for(int i=24;i>=0;i--)
        {
            if(father[x][i]!=father[y][i])
            {
                x=father[x][i];
                y=father[y][i];
            }
        }
    }
    else return x;
    return father[x][0];
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,f,t;
        scanf("%d",&n);
        init();
        for(int i=1;i<=n-1;i++)
        {
            scanf("%d%d",&f,&t);
            build(f,t);
        }
        for(int i=1;i<=n;i++)
        {
            if(!deep[i])
                dfs(i,i);
        }
        int x,y;
        scanf("%d%d",&x,&y);
        cout<<lca(x,y)<<endl;   
    }
    return 0;
}

/*
1
16
1 14
8 5
10 16
5 9
4 6
8 4
4 10
1 13
6 15
10 11
6 7
10 2
16 3
8 1
16 12
12 7

4
*/
/*
5
1 2
2 3
2 4
4 5
*/
  • 快速冪
    非遞歸版的快速冪就是通過倍增實現的,我比較習慣打非遞歸版的,代碼比較短而且給我一種很快的錯覺233。直接上代碼!(此代碼基於codevs 1497)(此題數據比較水,代碼比較naive,取膜取少了,注意要多取膜!)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
LL fast_pow(LL a,LL p,LL m)
{
    LL ans=1;
    for(;p;p>>=1,a=(a*a)%m)
        if(p&1)
            ans=(ans*a)%m;
    return ans;
}
int main()
{
    LL x,y,mod;
    scanf("%lld%lld%lld",&x,&y,&mod);
    cout<<x<<"^"<<y<<" "<<"mod"<<" "<<mod<<"=";
    printf("%lld",fast_pow(x,y,mod));
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章