連號區間數 Apare xzc

2021-09-29 14:32:07 字數 4119 閱讀 4335

問題描述

小明這些天一直在思考這樣乙個奇怪而有趣的問題:

在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很小的時候,小明可以很快地算出答案...