1. 程式人生 > 其它 >P7635 [COCI2010-2011#5] DVONIZ

P7635 [COCI2010-2011#5] DVONIZ

題目傳送門

演算法分析:尺取

看到本題,有些同學可能會想到二分。但實際上,答案並不滿足單調性。為了敘述方便,以下稱區間 \(i \to i+k-1\) 為左區間,區間 \(i+k \to i+2\times k-1\) 為右區間。在 \(k\) 增大時,左區間的值的確滿足單調性,但右區間的值有減有增,不滿足單調性。具體看以下樣例:

6 10
1 6 3 8 1 1

\(i=1 ,k=2\),時,右區間和大於 \(s\),不滿足條件,但當 \(k\)\(k=3\) 時,滿足條件。

由此可見,答案並不滿足單調性。

根據題意從左往右依次計算的時間複雜度為 \(\mathcal{O}(n^2)\)

,顯然不能滿足 \(N\le 10^5\) 的要求。於是我們考慮反過來,從右往左計算。每次計算 \(k\) 值時,可以利用上一次計算的結果進行調整,從而在較小時間內得到結果。記 \(r\) 為左區間的右端點,可以證明,在由 \(i\to i-1\) 的過程中,決策區間 \(i\to r\) 的右端點 \(r\)單調不增的。我們將該性質作為迴圈不變式證明演算法的正確性:

在第 \(i\) 次迴圈開始前,\(i+1 \to r\) 算得的 \(r\) 是當前可能的最大右邊界。即,在整個過程中,\(r\) 是單調不增的。

初始化:

\(i=n\) 時,顯然 \(k=0\) 是當前的最優解。

保持:

\(i+1\to i\) 時,存在兩種情況。

  • \(i\to r\) 仍符合條件,此時滿足 \(sum_r-sum_{l-1}\le s\)\(sum_{r+k}-sum_r\le s\)。若在情況 \(i+1\) 時,\(r\) 受到 \(r+k\le n\) 的限制而不能增大,那麼在當前狀態下,\(r\) 同樣受此限制。若 \(r+k<n\),假設 \(r\) 能增大,那麼 \(k\) 也隨之增大,於是左區間的和在前一次的基礎上繼續增大。在 \(r+k<n\) 的情況下,根據迴圈不變式,\(k\) 必定已經取到極限值,即左區間不能繼續向右擴張。這與 \(k\)
    增大矛盾,因此 \(r\) 同樣不會增大,迴圈不變式得到保持。
  • \(i\to r\) 不符合條件。那麼我們將 \(r\) 左移,左、右區間均隨之減小,且必定一個存在 \(r\) 使得 \(sum_r-sum_{l-1}\le s\)\(sum_{r+k}-sum_r\le s\)。且此時左區間恰滿足條件,此時滿足迴圈不變式。

終止:

顯然,$i \ge 1 $ ,在 \(i=1\) 之後,過程終止。

綜上所述,迴圈不變式得到證明,演算法正確性得到保證。同時,在演算法中, \(r\)\(i\) 的變化而單調不增,因此時間複雜度為 \(\mathcal{O}(n)\)

下面給出具體的程式碼:

#include<bits/stdc++.h>
#define reg register
#define ll long long
#define F(i,a,b) for(reg int i=a;i<=b;++i)
using namespace std;
inline int read();
const int N=1e5+1;
int n,s,sum[N],ans[N];
int main() {
	n=read(),s=read();
	F(i,1,n)sum[i]=sum[i-1]+read();//字首和 
	
	reg int r=n,k;
	for(reg int i=n; i; --i) {//從右往左計算 
		while(r>=i) {
			k=r-i+1;//r為左區間的右端點。 
			if(r+k<=n and sum[r]-sum[i-1]<=s and sum[r+k]-sum[r]<=s)break;
			--r;
		}
		ans[i]=r-i+1<<1;//計算出可行的長度。 
	}
	
	F(i,1,n)printf("%d\n",ans[i]); 
	return 0;
}
inline int read() {
	reg int x=0;
	reg char c=getchar();
	while(!isdigit(c))c=getchar();
	while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x;
}

AC

歡迎交流討論,請點個贊哦~