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

[COCI2010-2011#5] DVONIZ

[COCI2010-2011#5] DVONIZ

題意:

​ 定義長度為 \(2\times K\) 並且前 \(K\) 個元素之和不超過 \(S\) 且後 \(K\) 各元素之和也不超過 \(S\) 的數列為 INTERESTING 串,給定長度為 \(N\) 的數列 \(A\) ,對於每個元素,求出以該元素為起點的最長 INTERESTING 串。(\(2 \le N \le 100000,1\le S \le 2\times10^9\))

​ 首先我們定義 \(\text{mid}\) 為一個 INTERESTING 串的中心:即分界點。設 \(\text{L}\),\(\text{K}\) 分別為這個串的左端點和右端點,而這個串長度為 \(2\times K\)

我們把 \(\text{L}\) ~ \(\text{mid}\) 這一段看作串的前 \(K\) 個。而 \(\text{mid}\) ~ \(\text{R}\) 這一段看作為串的後 \(K\) 個。顯然,當 \(\text{mid}\) 不變時所有以 \(\text{mid}\) 為中心點的且長度小於 \(K\) 的串都是 INTERESTING 串。所以,我們只要找到對於每一個位置作為 \(\text{mid}\) 時所能取到的最長的長度 \(K\) 就可以通過遞推得到每個位置作為起點時的答案。

​ 遞推式為:

\[ans[i]=\max(ans[i-1]-2,ans[i]) (1\le i \le n) \]

​ 那麼問題變成了求對於每個位置作為 \(\text{mid}\)

時所能取到的最大的 \(K\)。 我們考慮從左到右列舉 \(\text{mid}\)。 注意到,當 \(\text{mid}\) 向右移動時,它能取到最遠的左端點和右端點\(L,R\) 必定也會向右邊移動。那麼我們就可以用雙指標尺取)的方法來解決這個問題。時間複雜度為 \(O(n)\)

​ 在取完之後,我們根據左右指標所在的位置 \(L,R\) 來得到此時能取到的最大的 \(K\)。 即:

\[K=\min(R-mid,mid-l+1) \]

​ 而更新答案我們這樣更新:

\[ans[mid-K+1]=2\times K \]

​ 在結束後我們再遍歷一遍,通過上文得到的遞推式推出所有的 \(\text{ans}\)

值。

程式碼如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 1e5+5;
int n;
ll s,a[MAXN],len[MAXN];
int main()
{
	scanf("%d %lld",&n,&s);
	for(int i=1;i<=n;++i)
		scanf("%lld",&a[i]);
	int l=1,r=0;
	ll ls=0,rs=0;
	while(rs+a[r+1]<s&&r<n) rs+=a[++r];
	for(int i=1;i<=n;++i)
	{
		ls+=a[i];
		rs-=a[i];
		while(ls>s&&l<=i)
		{
			ls-=a[l];
			++l;
		}
		while(rs+a[r+1]<=s&&r<n) rs+=a[++r];
		int le=min(i-l+1,r-i);
		if(le>0) len[i-le+1]=le*2;
	}
	for(int i=1;i<n;++i) len[i]=max(len[i-1]-2,len[i]);
	for(int i=1;i<n;++i)
		printf("%lld\n",len[i]);
	printf("0\n");
	return 0;
}