现在的位置: 首页 > 综合 > 正文

Codeforces Problemset 212D(VK Cup 2012 Finals (unofficial online-version))

2018年01月15日 ⁄ 综合 ⁄ 共 1736字 ⁄ 字号 评论关闭
问题描述
  小v家有一条护栏,由n个木板组成。第i个木板的高度是a[i]。现在小镇上流行给护栏画矩形,所以小v也要在自家的护栏上画。若要在区间[x,x+k-1]这个区间画一个宽度为k的矩形(1<=x<=n-k+1),为了美观,高度一定是这个区间里高度最低的木板。现在小v有m个心中理想的宽度,第i个为ki,(ki与kj之间可能一样,1<=i<=n,1<=j<=n)他想知道对于每个ki,求矩形高度的期望。
输入格式
  第一行一个整数n,表示木板的数目。第2行有n个数,第i个数ai表示第个木板的高度。第3行一个整数m。第4行有m个数,表示小v心中理想的第i个宽度ki
输出格式
  输出m行实数,第i行表示宽度为ki的 矩形高度的期望,只要你的答案和正确答案的差的绝对值小于10的-9次方,你的答案将被视为正确。
样例输入
3

3 2 1

4

1 2 3 1
样例输出
2.000000000000000

1.500000000000000

1.000000000000000

2.000000000000000
数据规模和约定
  1 ≤ n ≤ 10

  1 ≤ ai ≤ 10

  1 ≤ m ≤ 106 

  1 ≤ ki ≤ n

长度N的序列,所有位置到权值不大于他的位置距离和是nlogn级的……这个很显然。

然后我们高度从小到大来求有哪些长度的区间高度是这个。

我们需要知道他左方和右方距离他最近的在他之前计算过的点在哪。

这个线段树可以logn解决……我写的二分加树状数组log^2n的……

然后枚举起点,这个枚举的范围在距离近的那边,然后发现是对一段区间加同一个数……而且最后问,那就等价于对前缀和操作。

之前证明了距离和是nlogn级的,所以这个操作的复杂度就均摊为nlogn。

然后最后O(1)回答询问就行了。

算法瓶颈在于线段树或者二分加树状数组部分……线段树的话是nlogn的……二分加树状数组nlog^2n……二分加树状数组常数比较小还给了8s是可以过的。

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,i,j,k,l,r,mid,L,R,len,x;
int where[1000011],a[1000011],dl[1000011],tree[1000011];
long long s[1000011];
bool cmp(int x,int y)
{
		 return a[x]<a[y];
}
int lowbit(int x)
{
		return x-(x&(x-1));
}
void ins(int x)
{
		 while (x<=n)
		 {
		 			 tree[x]++;
		 			 x+=lowbit(x);
		 }
}
int qsum(int x)
{
		int ans=0;
		while (x)
		{
					ans+=tree[x];
					x-=lowbit(x);
		}
		return ans;
}
int main()
{
		std::ios::sync_with_stdio(false);
		cin >> n;
		for (i=1;i<=n;i++) cin >> a[i],dl[i]=i;
		sort(dl+1,dl+n+1,cmp);
		for (i=1;i<=n;i++)
		{
				l=1,r=dl[i];
				while (l!=r)
				{
							mid=(l+r)/2;
							if (qsum(dl[i])-qsum(mid-1)==0) r=mid;
							else														l=mid+1;
				}
				L=l;
				l=dl[i],r=n;
				while (l!=r)
				{
							mid=l+r-(l+r)/2;
							if (qsum(mid)-qsum(dl[i]-1)==0) l=mid;
							else												 		r=mid-1;	
				}
				R=r;
				ins(dl[i]);
				if (dl[i]-L<R-dl[i]) x=dl[i]-L+1,len=R-dl[i];
				else								 x=R-dl[i]+1,len=dl[i]-L;
				for (j=1;j<=x;j++)
						s[j]+=a[dl[i]],s[j+len+1]-=a[dl[i]];
		}
		for (i=1;i<=n;i++)
				s[i]+=s[i-1];
		cin >> m;
		for (i=1;i<=m;i++)
		{
				cin >> k;
				printf("%.11f\n",s[k]/(double)(n-k+1));
		}
		return 0;
}

抱歉!评论已关闭.