算法競賽進階指南0x6 倍增

二分與倍增

倍增的定義請參考李煜東大佬的書,我們這裏給出二分和倍增的模板。

二分:

int l=0,r=v.size()-1;
while(l<r)
{
	int mid=l+r>>1;
	if(v[mid]>=x) r=mid;
	else l=mid+1;
}

倍增:

int r=0,p=1;
while(p)
{
	if(r+p<v.size() && v[r+p]<=x) r+=p,p<<=1;
	else p>>=1;
}

我可以觀察到其實倍增是二分的逆運算,二分與x在數組的位置無關,但倍增卻是與x有關的,二分的時間複雜度穩定在(lognlogn),倍增在最優O(1),最差(2logn2logn),普通情況下用二分就夠了,一般來說二分比較快,但也有例外,比如測試集中存在大量的比較近的x,我們可以用倍增。

天才ACM

給定一個整數 M,對於任意一個整數集合 S,定義“校驗值”如下:

從集合 S 中取出 M 對數(即 2∗M 個數,不能重複使用集合中的數,如果 S 中的整數不夠 M 對,則取到不能取爲止),使得“每對數的差的平方”之和最大,這個最大值就稱爲集合 S 的“校驗值”。

現在給定一個長度爲 N 的數列 A 以及一個整數 T。

我們要把 A 分成若干段,使得每一段的“校驗值”都不超過 T。

求最少需要分成幾段。

輸入格式
第一行輸入整數 K,代表有 K 組測試數據。

對於每組測試數據,第一行包含三個整數 N,M,T 。

第二行包含 N 個整數,表示數列A1,A2…AN。

輸出格式
對於每組測試數據,輸出其答案,每個答案佔一行。

數據範圍
1≤K≤12,
1≤N,M≤500000,
0≤T≤1018,
0≤Ai≤220
輸入樣例:
2
5 1 49
8 2 1 7 9
5 1 64
8 2 1 7 9
輸出樣例:
2
1

這題的思路就是不斷歸併集合,並用倍增優化。我們可以看到即使我們最壞時間複雜度高達(nlogn)依然能過,是因爲倍增優化了許多離原點很近的方案。

#include<iostream>
#include<cstring>
#include<algorithm>

#define ll long long

using namespace std;

const int N=500010;

int a[N],t1[N],t2[N];
int n,m;
ll s;

bool check(int l,int mid,int r)
{
    for(int i=mid;i<=r;i++) t1[i]=a[i];
    sort(t1+mid,t1+r+1);
    int i=l,j=mid;
    for(int k=l;k<=r;k++)
    {
        if(j>r||(i<mid&&t1[i]<=t1[j])) t2[k]=t1[i++];
        else t2[k]=t1[j++];
    }   
    ll res=0;
    int left = l,right=r,cnt=0;
    while(cnt<m&&right>left)
    {
        res+=1ll * (t2[right]-t2[left]) * (t2[right] - t2[left]);
        right--,left++,cnt++;
    }
    if(res<=s)
    {
        for(int k=l;k<=r;k++) t1[k]=t2[k];
        return true;
    }
    return false;
}
int main(){
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%lld",&n,&m,&s);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        int l=1,r=1,res=0;
        while(l<=n)
        {
            int p=1;
            t1[l]=a[l];
            res++;
            while(p)
            {
                if(r+p<=n&&check(l,r+1,r+p)) r+=p,p<<=1;
                else p>>=1;
            }
            l=++r;   
        }
        printf("%d\n",res);
    }


    return 0;
}

ST算法

給大家推薦一下poj 3264。給大家寫一下代碼,思路請參考李煜東大佬。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>

using namespace std;

const int N=100010;

int a[N];
int fs[N][50],fb[N][50];
int n;
void work()
{
    for(int i=1;i<=n;i++){
        fs[i][0]=a[i];
        fb[i][0]=a[i];
    }
    int t=log(n)/log(2)+1;
    for(int j=1;j<t;j++)
        for(int i=1;i<=n;i++)
        {
            fb[i][j]=max(fb[i][j-1],fb[i+(1<<j-1)][j-1]);
            fs[i][j]=min(fs[i][j-1],fs[i+(1<<j-1)][j-1]);
        }
}

int query(int l,int r)
{
    int k=log(r-l+1)/log(2);
    int MAX=max(fb[l][k],fb[r-(1<<k)+1][k]);
    int MIN=min(fs[l][k],fs[r-(1<<k)+1][k]);
    return MAX-MIN;
}
int main(){
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    int T;
    scanf("%d%d",&n,&T);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);

    work();
    
    while(T--)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        printf("%d\n",query(l,r));
    }


    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章