1. 程式人生 > >塊狀陣列+歸併樹學習 poj2104+hdu2665

塊狀陣列+歸併樹學習 poj2104+hdu2665

今天在大白(挑戰程式設計)上重新看了一遍分塊陣列相關的內容,雖然以前看過了,也做過相關的題目,但是總感覺只知道思想,自己不敢實現,今天把上面的例題照著敲了敲,也算加深了印象吧。

這兩個是題都是求區間第K大,只不過HDU上資料強並且時間限制少了點。

以前以為平方分割求這種題還挺快的,沒想到啊。。在POJ  11000+ms勉強水過,在HDU無情T掉。

不過歸併樹倒是有些出乎我意料的快,POJ  6000+ms,HDU 3500+ms

歸併樹之所以叫歸併樹是因為這一整棵樹剛好是歸併排序的完整再現。

今天除了溫習了這兩種資料結構,新收穫是get了庫函式merge的用法,以及!以後在這兩個OJ上交題千萬注意的一

點 ! 當你交C++  T了的時候不妨交一發G++試一試,極大可能會有驚喜,上面這兩種演算法我交C++都是TLE,很氣。

PS:區間第K大一類的問題好像是主席樹裸題,看人家400+ms輕鬆跑過的題而我要跑11000+ms,真的是欲哭無淚啊,改天膜拜一下主席樹回來再戰!

poj  的塊狀陣列程式碼:

#include<iostream>
#include<algorithm>
#include<vector>
#include<stdio.h>
#include<math.h>
using namespace std;
const int MAXN=100010;
const int B=1000;
vector<int>bucket[MAXN];
int a[MAXN],tmp[MAXN];
void init(int n)
{
    for(int i=0;i<n;i++)
    {
        bucket[i/B].push_back(a[i]);
        tmp[i]=a[i];
    }
    sort(tmp,tmp+n);
    for(int i=0;i<n/B;i++)
        sort(bucket[i].begin(),bucket[i].end());
}
int check(int mid,int tl,int tr)
{
    int x=tmp[mid];
    int cnt=0;
    //區間兩端多出的部分
    while(tl < tr && tl % B) if(a[tl++] <= x) cnt++;
    while(tl < tr && tr % B) if(a[--tr] <= x) cnt++;
    //對每一個桶進行計算
    while(tl < tr)
    {
        int b = tl / B;
        cnt += upper_bound(bucket[b].begin(), bucket[b].end(),x) - bucket[b].begin();
        tl += B;
    }
    return cnt;
}
int solve(int L,int R,int K,int n)
{
    int l=0,r=n-1,mid;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(check(mid,L,R+1) >= K)
        r = mid-1;
        else
        l = mid+1;
    }
    //cout<<l<<" "<<r<<endl;
    return tmp[r+1];
}
int main()
{
    int n,m,l,r,k,T;
    //cin>>T;
    //while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
            scanf("%d",a+i);
        init(n);
        while(m--)
        {
            scanf("%d%d%d",&l,&r,&k);
            printf("%d\n",solve(l-1,r-1,k,n));
        }
    }
}
HDU的歸併樹:
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<vector>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
const int MAXN = 100010;
int a[MAXN];
vector<int>tree[MAXN<<2];
void build(int l,int r,int rt)
{
	if(l==r)
	{
		tree[rt].push_back(a[l]);
		return ;
	} 
	int mid=(l+r)>>1;
	build(lson);
	build(rson);
	tree[rt].resize(r-l+1);
	merge(tree[rt<<1].begin(),tree[rt<<1].end(),tree[rt<<1|1].begin(),tree[rt<<1|1].end(),tree[rt].begin());
}

int query(int L,int R,int x,int l,int r,int rt)
{
	if(L<=l&&r<=R)
	{
		return upper_bound(tree[rt].begin(),tree[rt].end(),x)-tree[rt].begin(); 
	}
	int mid=(l+r)>>1,sum=0;
	if(L<=mid)
	sum+=query(L,R,x,lson);
	if(R>mid)
	sum+=query(L,R,x,rson);
	return sum;
}
int tmp[MAXN];
int solve(int L,int R,int k,int n)
{
	int l=1,r=n,mid;
	while(l<=r)
	{
		mid=(l+r)>>1;
		if(query(L,R,tmp[mid],1,n,1) >= k)
		r=mid-1;
		else
		l=mid+1;
	}
	return tmp[r+1];
}
int main()
{
	int n,m,l,r,k;
	int T;
	cin>>T;
	while(T--)
	{
		cin>>n>>m;
		for(int i=1;i<=n;i++)
		scanf("%d",a+i),tmp[i]=a[i];
		build(1,n,1);
		sort(tmp+1,tmp+n+1);
		while(m--)
		{
			scanf("%d%d%d",&l,&r,&k);
			printf("%d\n",solve(l,r,k,n));
		}
		for(int i=0;i<MAXN*4;i++)
		tree[i].clear();	
	}
}