题解 CF212D 【Cutting a Fence】

LuckyCloud

2018-07-05 19:32:44

Solution

~~真的,已经不想说什么了,这道题出的真的太好了……orzorzorz~~ - 对于每个数a[x]在它左边找一个离它最近的小于它的数,在它右边找一个离它最近 的小于等于它的数 记所找到左边数的位置为 _l_ ,所找到右边数的位置为 _r_ 。 **那么在[ _l_ +1, _r_ -1]中的所有子区间,只要包含a[x],最小值都是a[x]** - 设a[x]到a[ _l_ +1]长度为L,a[x]到a[ _r_ -1]长度为R 当第二问的区间长度为K时(K是个变量),把所有长度为K的区间最小值的和设为Sum 我们开始分类讨论 设L<=R ① 当1<=K<=L,会有K个长度为K的区间的最小值是a[x],Sum=Sum+K*a[x] ② 当L+1<=K<=R 会有L个长度为K的区间的最小值是a[x],Sum=Sum+L*a[x] ③ 当R+1<=K<=L+R-1 会有L+R-K个长度为K的区间的最小值a[x], Sum=Sum+a[x] (L+R-K)=Sum+a[x] (L+R)-K*a[x] 对于每一个a[x],我们需要把它**对所有区间长度询问的答案贡献**求出 也就是对于每个Len(1<=len<=L+R-1),我们都要根据上面分类讨论得出的结论, 算出对Sum的贡献。观察关于Sum的式子可以发现,只有K*a[x]才是一个会变化的值, 其余都是定值。 我们不妨设C1[K]是区间长度为K时,所有需要乘K的a[x]的和,C2[K]是区间长度为K时,所有定值的和。 设ans[K]是区间长度为K的答案,ans[K]=(K*C1[K]+C2[K])/(n-K+1) 我们需要预处理出所有区间长度的答案,然后O(1)解出每一个询问。 显然对于每一个a[x] (1<=x<=n),都会有一系列的K满足前面的三种讨论情况, 而每一个K,都会有一个C1[K],C2[K],会发现,当连续的几个K都处于同一种情况的时候,所对应的C1[],C2[]增加的值是相等的,这就可以看作是区间加,可以用差分来做。 For Example 2 5 5 5 3 5 5 5 5 5 5 2 a[6]=3, L=4, R=7 当K取[1,4]时,对于a[6]会有4个区间满足最小值是a[6]的条件 那么 C1[1]=C1[1]+a[6], C1[2]=C1[2]+a[6], C1[3]=C1[3]+a[6], C1[4]=C1[4]+a[6] ——显然一个区间加 当K取[5,7]时,对于a[6]会有5个区间满足最小值是a[6]的条件 那么 C2[5]=C2[5]+4*a[6] C2[6]=C2[6]+4*a[6] C2[7]=C2[7]+4*a[6] ——显然一个区间加 当K取[8,10]时,对于a[6]会有11-K个区间满足最小值是a[6]的条件 那么 C1[8]=C1[8]+11*a[6] C1[9]=C1[9]+11*a[6] C1[10]=C1[10]+11*a[6] ——显然一个区间加 C2[8]=C2[8]-a[6] C2[9]=C2[9]-a[6] C2[10]=C2[10]-a[6] ——显然一个区间加 至于为什么对于每个数a[x]一个是找离它最近的小于它的数,另一个是找右边离它最近的小于等于它的数? 同一个区间内最小值有多个的话,也只需要算一次,比如样例二的这种数据是会被多次计算的,[1,2]这个区间,a[1]的时候算一次,a[2]的时候算一次,所以为什么要这么做,你可以感性的理解为去重。 OK,OK,其余就看代码吧! ```cpp #include<cstdio> #include<algorithm> #include<cstring> using namespace std; long long n,m,R[1000100],a[1000100],k,x,L[1001000],y,C1,C2; long long c1[1001000],c2[1001000]; double ans[1001000]; inline long long read() { long long cnt=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-')f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){cnt=cnt*10+ch-48;ch=getchar();} return cnt*f; } int main() { n=read(); for (int i=1;i<=n;i++) a[i]=read(); a[n+1]=-((long long)1<<60); a[0]=-((long long)1<<60); for (int i=n;i;i--) { if (a[i+1]<=a[i]) R[i]=i+1; else for (int j=R[i+1];j<=n+1;j=R[j]) if (a[j]<=a[i]) {R[i]=j;break;} } for (int i=1;i<=n;i++) { if (a[i-1]<a[i]) L[i]=i-1; else for (int j=L[i-1];j>=0;j=L[j]) if (a[j]<a[i]) {L[i]=j;break;} } for (int i=1;i<=n;i++) { L[i]=i-L[i]; R[i]=R[i]-i; } for (int i=1;i<=n;i++) { x=L[i];y=R[i]; if (x>y) swap(x,y); c1[1]+=a[i];c1[x+1]-=a[i]; c2[x+1]+=a[i]*x;c2[y+1]-=a[i]*x; c2[y+1]+=a[i]*(x+y);c2[x+y]-=a[i]*(x+y); c1[y+1]-=a[i];c1[x+y]+=a[i]; } for (int i=1;i<=n;i++) { C1+=c1[i]; C2+=c2[i]; ans[i]=((C1*i+C2)*1.0)/((n-i+1)*1.0); } m=read(); for (int i=1;i<=m;i++) { k=read(); printf("%.9lf\n",ans[k]); } return 0; } ``` LuckyCloud