問題描述
小明這些天一直在思考這樣乙個奇怪而有趣的問題:
在1~n的某個全排列中有多少個連號區間呢?這裡所說的連號區間的定義是:
如果區間[l, r] 裡的所有元素(即此排列的第l個到第r個元素)遞增排序後能得到乙個長度為r-l+1的「連續」數列,則稱這個區間連號區間。
當n很小的時候,小明可以很快地算出答案,但是當n變大的時候,問題就不是那麼簡單了,現在小明需要你的幫助。
輸入格式
第一行是乙個正整數n (1 <= n <= 50000), 表示全排列的規模。
第二行是n個不同的數字pi(1 <= pi <= n), 表示這n個數字的某一全排列。
輸出格式
輸出乙個整數,表示不同連號區間的數目。
樣例輸入1
43 2 4 1
樣例輸出1
7樣例輸入2
53 4 2 5 1
樣例輸出2
9
連號區間數:n<=5e4
給定乙個n的排列,求有多少個區間滿足區間排序後為連續的數列
有乙個比較容易想到的o(n^2)的演算法: 1 2 3 4 5
遍歷排列的每個區間,如果區間的最大值maxx減去最小值minn等於區間長度減一,那麼這個區間排序後就能成為連續的數列
偽**如下:
ans = 0
for left in range(1,n+1):
maxx = minn = a[left]
for right in range(left,n+1): #當前的區間為a[left],a[left+1],a[left+2],...a[right]
maxx = max(maxx,a[right])
minn = min(minn,a[right])
if maxx-minn == right-left:
ans += 1
第二種思路,參照codeforces 193d(two segments)的做法:
定義函式f(l,r)為數字l,l+1,l+2...r-1,r 這個連續的數列在給定的排列中被分割成的段數
比如輸入為 1 5 3 4 2 6,那麼[1,3]被分割成了3段,[2,4]被分割成1段,[5,6]被分割成2段
那麼當l<=r 且 f(l,r)為 1 的時候,給定的排列中存在一段可以排序後構成l,l+1,l+2,...r-1,r這個連續的數列,那麼ans+=1
我們現在列舉排序後組成區間的左右端點l和r,如果 f(l,r)為 1,那麼答案+1
那麼我們考慮狀態的轉移:已知 f(l,r)==k如何求得 f(l,r+1)呢?
我們考慮r+1這個數在排列**現的位置:
1.如果r+1這個數和[l,r]這個數列在排列中被分割成的任何一段都不相鄰,那麼 f(l,r+1) = f(l,r)+1 = k+1
2.如果r+1這個數和[l,r]這個數列在排列中被分割成的某一段相鄰,那麼f(l,r+1) = f(l,r) = k
3.如果r+1這個數連線了[l,r]這個數列在排列中被分割的某兩段,那麼f(l,r+1) = f(l,r)-1 = k-1
如果k<=2那麼我們從f(l,r)到f(l,r+1)的轉移就是o(1)的
基於這個思路,我們可以兩重迴圈遍歷l和r,得到乙個o(n^2)的演算法:
for l in range(1,n+1):
map,int> mp;
mp.insert( make_pair(make_pair(1,1),1) )
pre = f[l][l] = 1; ans += 1
for r in range(l+1,n+1):
p = pos[r];
for(auto x:mp):
。。。不太清楚要咋搞~
我們要計算的是:
sum我們可以從1到n遍歷排序後的區間右端點r,然後用線段樹處
理出f(1,r),f(2,r),f(3,r),...f(r,r)的資訊
複雜度為o(nlogn)
在第二重迴圈每次結束後,我們都需要統計一次f(1,r),f(2,r),f(3,r),
...,f(r,r)的所有資訊,把值為1的個數統計出來。我們用線段樹來維護這些函式值的資訊,
因為f(x,y)的最小值為1,我們要求的就是f(x,y)為1的個數,所以我們可以用線段樹來維護
區間最小值,以及等於最小值的數的個數。線段樹的某一段[left,right]記錄的是f(left,r)
,f(left+1,r),...f(left+2,r),f(right-1,r),f(right,r)這幾個函式的最小值和等於最小值的個數
我們只需要查詢區間最小值等於1的個數並累加即可
每次迴圈的時候r+1,但目前記錄的都是右端點等於r的資訊,我們要做一些狀態的轉移
對於排序後的區間左端點i(1<=i<=r+1)來說,都要從線段樹維護的都是f(i,r),都要轉化
為f(i,r+1)
剛才我們已經分析過了r+1這個數的位置對於f(x,r)轉移到f(x,r+1)的影響
我們可以先假設r+1這個數出現的位置和1,2,3...r都不相鄰,那麼對於i from 1 to r,就
都有: f(i,r+1) = f(i,r) + 1, 我們可以用線段樹進行區間加1,add(1,r,1)
但是r+1在排列中的位置左右兩邊可能會有小於r+1的數與之相鄰,設為x,y,這樣的話,如果x/*
提交序號 姓名 試題名稱 提交時間 **長度 cpu使用 得分 cpu使用 記憶體使用
3835862 許智超 連號區間數 11-13 14:56 1.874kb c++ 正確 100 0ms 3.605mb
*/#include
using
namespace std;
const
int maxn =
5e4+10;
#define ll long long
int min[maxn*4]
,tot[maxn*4]
,lazy[maxn*4]
,a[maxn]
,pos[maxn]
;int
build
(int left,
int right,
int pos)
;void
push_up
(int pos)
;void
push_down
(int pos)
;int
query
(int left,
int right,
int l,
int r,
int pos)
;void
update
(int left,
int right,
int l,
int r,
int add,
int pos)
;int
main()
printf
("%lld\n"
,ans);}
return0;
}void
push_down
(int pos)
void
push_up
(int pos)
intbuild
(int left,
int right,
int pos)
void
update
(int left,
int right,
int l,
int r,
int add,
int pos)
//區間都加1
push_down
(pos)
;int mid =
(left+right)/2
;update
(left,mid,l,r,add,pos*2)
;update
(mid+
1,right,l,r,add,pos*2+
1);push_up
(pos);}
intquery
(int left,
int right,
int l,
int r,
int pos)
連號區間數
小明這些天一直在思考這樣乙個奇怪而有趣的問題 在1 n的某個全排列中有多少個連號區間呢?這裡所說的連號區間的定義是 如果區間 l,r 裡的所有元素 即此排列的第l個到第r個元素 遞增排序後能得到乙個長度為r l 1的 連續 數列,則稱這個區間連號區間。當n很小的時候,小明可以很快地算出答案,但是當n...
連號區間數
小明這些天一直在思考這樣乙個奇怪而有趣的問題 在1 n的某個全排列中有多少個連號區間呢?這裡所說的連號區間的定義是 如果區間 l,r 裡的所有元素 即此排列的第l個到第r個元素 遞增排序後能得到乙個長度為r l 1的 連續 數列,則稱這個區間連號區間。當n很小的時候,小明可以很快地算出答案,但是當n...
連號區間數
問題描述 小明這些天一直在思考這樣乙個奇怪而有趣的問題 在1 n的某個全排列中有多少個連號區間呢?這裡所說的連號區間的定義是 如果區間 l,r 裡的所有元素 即此排列的第l個到第r個元素 遞增排序後能得到乙個長度為r l 1的 連續 數列,則稱這個區間連號區間。當n很小的時候,小明可以很快地算出答案...