1. 程式人生 > >bzoj3316 JC loves Mkk題解

bzoj3316 JC loves Mkk題解

long div 完全 opened 數據 memset 一個點 alt php

3316: JC loves Mkk

Time Limit: 5 Sec Memory Limit: 64 MB
Submit: 979 Solved: 316
[Submit][Status][Discuss]

Description

技術分享

Input

第1行,包含三個整數。n,L,R。
第2行n個數,代表a[1..n]。

Output


僅1行,表示詢問答案。
如果答案是整數,就輸出整數;否則,輸出既約分數“P/Q”來表示。

Sample Input

5 3 4
3 1 2 4 5

Sample Output

7/2

HINT
1≤L≤R≤n≤10^5,0≤ai≤10^9,保證問題有解,數據隨機生成
  這道題其實挺有意思的,既約分數這一點恐怕卡住了無數想打二分的像我一樣的蒟蒻,然而,如果我們觀察到問題的本質我們就可以拋開既約分數對於二分答案的限制。   所以,為了方便,我們先假設這道題只是讓我們輸出小數,那麽假設我們二分出來的答案是x,l為我們選擇的左邊界則:     sum[i]-sum[l-1]/(i-l+1)>=x   (L<=i-l+1<=R (i-l+1)%2==0)   將式子化簡可得sum[i]-i*x>=sum[l-1]-(l-1)*x那麽每一個點所提供的信息在二分答案確定後也就可以確定了。值得註意的是由於題目偶數的限制,我們需要準備兩個單調隊列去滿足這一條件,利用滾動數組即可,同時,我們也應註意到,我們實際要求的是sum[l-1]-(l-1)*x最小,對於l自己本身並無要求,所以我們單調隊列存的實際是l-1的信息。
  現在,除了既分約數基本這道題解決完了。我們可以註意到,對於每次check二分的值成功時他所對應的答案一定是遞增的,所以我們完全可以拋開我們二分的值,直接現求當前答案,這樣就可以很好的解決既分約數對於二分答案的限制了。    技術分享
 1 #pragma GCC optimze("O3")
 2 #include<iostream>
 3 #include<cstdlib>
 4 #include<cstdio>
 5 #include<cstring>
 6 #include<queue>
 7 #include<algorithm>
 8
#include<cmath> 9 #include<map> 10 #include<vector> 11 #define N 100005 12 using namespace std; 13 int n,L,R; 14 long long a[2*N],sum[N*2],mx; 15 int q[2][N*2]; 16 int hea[2],en[2]; 17 long long ansa,ansb; 18 long long gcd(long long x,long long y) 19 { 20 if(y==0)return x; 21 return gcd(y,x%y); 22 } 23 bool check(double x) 24 { 25 memset(q,0,sizeof(q)); 26 hea[0]=hea[1]=1,en[0]=en[1]=0; 27 int now=1,la=0; 28 for(int i=L;i<2*n;i++) 29 { 30 swap(now,la); 31 while(hea[now]<=en[now]&&sum[q[now][en[now]]]-(double)(q[now][en[now]])*x>sum[i-L]-(double)(i-L)*x) en[now]--; 32 en[now]++; 33 q[now][en[now]]=i-L; 34 if(i>R) 35 while(hea[now]<=en[now]&&q[now][en[now]]<i-R)hea[now]++; 36 if(sum[i]-sum[q[now][hea[now]]]>=(double)(i)*x-(double)(q[now][hea[now]])*x) 37 { 38 ansa=sum[i]-sum[q[now][hea[now]]]; 39 ansb=i-q[now][hea[now]]; 40 long long t=gcd(ansa,ansb); 41 ansa/=t; 42 ansb/=t; 43 return 1; 44 } 45 } 46 return 0; 47 } 48 int main() 49 { 50 scanf("%d%d%d",&n,&L,&R); 51 if(L%2)L++; if(R%2)R--; 52 for(int i=1;i<=n;i++) 53 { 54 scanf("%lld",&a[i]); 55 a[i+n]=a[i]; 56 mx=max(mx,a[i]); 57 } 58 for(int i=1;i<n*2;i++) 59 sum[i]+=sum[i-1]+a[i]; 60 double li=0,ri=mx; 61 while(ri-li>1e-6) 62 { 63 double mid=(li+ri)/2; 64 if(check(mid)) li=mid; 65 else ri=mid; 66 } 67 if(ansb!=1) 68 printf("%lld/%lld\n",ansa,ansb); 69 else 70 printf("%lld\n",ansa); 71 return 0; 72 }
View Code

bzoj3316 JC loves Mkk題解