1. 程式人生 > >【解題報告】HDU 4638 Group

【解題報告】HDU 4638 Group

/*
	http://acm.hdu.edu.cn/showproblem.php?pid=4638
	HDU 4638 Group - 樹狀陣列
	
	題意:
	求一段區間連續數字的段數
    [1 3 5 4 2] 詢問(2,4)區間則3,5,4為連續序列輸出 1
	解法:
	定義 線段 為 連續的數字段
	定義 改變數deta 為 新增一個數字之後區間中線段增加或者減少了幾個(其實就是-1,0,+1)
	定義 deta(n) 為 數字n在序列中產生的改變數
	  [2  3  5  4  1]
deta  +1  0 +1 -1  0
	我們如果在這個時候詢問(1,1)(1,2)(1,3)(1,4)(1,5)這些從1開始的區間都好辦
	只要將deta累加一下就好了(字首和嘛)
	但是題目偏要刁鑽一下,問個(2,5)區間怎麼辦?
	我們發現如果能將 1號位置上的那個數字對後面所產生的影響 消除 
	使得deta還是原來的含義該多好
	仔細分析一下這個例子中的第一個數字 和與它在數字上相鄰的兩個數字
	本身   2,deta=1
	相鄰   3,deta=0  --先出現
    相鄰   1,deta=0  --後出現

	根據deta的含義,在這個例子中,我們能得出如果在3之前有4出現的話那麼deta(3)一定為-1,
	而現在為0說明之前沒有出現過4,那麼我們要刪除2的話,當新增3時一定會增加一個“線段”(2意味著一個孤立的點)
	所以我們將deta(3)+=1
	deta(1)的操作同理deta(1)+=1;
	另外我們每次在後面找 在數字上與本身相鄰的數字 ,那麼不可能找出一個deta(i)=1的情況,這樣的話就不會與本身相鄰了
	更新完deta後 求詢問的區間和 就是答案
	至此這個問題就能用樹狀陣列完美解決了。
	參考 http://blog.csdn.net/no__stop/article/details/9716487  
*/

#pragma comment (linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;

const int N = 1e5 + 10;
struct BIT{ // 樹狀陣列
	int t[N],n;
	BIT(int n):n(n){memset(t,0,sizeof(t));}
	BIT(){}
	int lowbit(int x){
		return x&(-x);
	}
	void update(int x,int v){
		while(x<=n){
			t[x] += v;
			x += lowbit(x);
		}
	}
	int getsum(int x){
		int ret = 0;
		while(x > 0){
			ret += t[x];
			x -= lowbit(x);
		}
		return ret;
	}
};

struct Interval{  // 區間
	int l,r,index;
	bool operator < (const Interval& V)const{
		return l < V.l;
	}
};

int main(){
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int T;
	cin >> T;
	while(T--){
		int n,nq;
		Interval inte[N];      
		BIT t(n);
		int num[N],		      // number
			id[N]={0},        // position
			ans[N]={0};

		scanf("%d%d",&n,&nq);
		id[0] = id[n+1] = 0;
		for(int i = 1 ; i <= n ; i++){
			scanf("%d",&num[i]);
			id[num[i]] = i;
			if(id[num[i]-1] && id[num[i]+1])
				t.update(i,-1);
			else if(!id[num[i]-1] && !id[num[i]+1])
				t.update(i, 1);
			else 
				t.update(i, 0);
		}
		for(int i = 1 ; i <= nq ; i++){
			scanf("%d%d", &inte[i].l , &inte[i].r);
			inte[i].index = i;
		}
		sort(inte + 1 , inte + nq + 1);
		int pleft = 1;
		for(int i = 1 ; i <= nq ; i++){
			while(pleft < inte[i].l ){ // 相鄰的兩個更新一下
				if(num[pleft]<n)  t.update(id[num[pleft]+1] , 1);
				if(num[pleft]>1)  t.update(id[num[pleft]-1] , 1);
				pleft++;
			}
			ans[inte[i].index] = t.getsum(inte[i].r) - t.getsum(inte[i].l-1);
		}
		for(int i = 1 ; i <= nq ; i++){
			printf("%d\n",ans[i]);
		}
	}
	return 0;
}