1. 程式人生 > >bzoj3316: JC loves Mkk(單調佇列+分數規劃)

bzoj3316: JC loves Mkk(單調佇列+分數規劃)

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,保證問題有解,資料隨機生成
    首先這是一個分數規劃,於是我們得二分,設答案為mid,那麼原數列變成a[i]-mid,然後就是要找一段使得區間和大於0
字首和可以先預處理,然後找到滿足s[j]<s[i]且i<j的j,發現滿足條件的j中s[j]越小越好,於是用單調佇列維護 然後得保證選的數的個數是偶數,於是開兩個單調佇列,分別維護位置為奇數和偶數的
 1 //minamoto
 2 #include<iostream>
 3 #include<cstdio>
 4 #define ll long long
 5 using namespace std;
 6 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 7
char buf[1<<21],*p1=buf,*p2=buf; 8 template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;} 9 template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;} 10 int read(){ 11 #define num ch-'0' 12 char ch;bool flag=0;int res; 13
while(!isdigit(ch=getc())) 14 (ch=='-')&&(flag=true); 15 for(res=num;isdigit(ch=getc());res=res*10+num); 16 (flag)&&(res=-res); 17 #undef num 18 return res; 19 } 20 const int N=1e5+5; 21 int n,m,L,R,h1,h2,t1,t2;ll ans1,ans2,g,A[N<<1],S[N<<1]; 22 double v[N<<1],s[N<<1];int q1[N<<1],q2[N<<1]; 23 ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} 24 bool check(double x){ 25 for(int i=1;i<=m;++i) v[i]=A[i]-x,s[i]=s[i-1]+v[i]; 26 h1=h2=t1=1,t2=0,q1[1]=0; 27 for(int i=L;i<=m;++i){ 28 while(h1<=t1&&q1[h1]<i-R) ++h1; 29 while(h2<=t2&&q2[h2]<i-R) ++h2; 30 if(!(i&1)&&h1<=t1&&s[q1[h1]]<=s[i]){ 31 ans1=S[i]-S[q1[h1]],ans2=i-q1[h1],g=gcd(ans1,ans2),ans1/=g,ans2/=g;return 1; 32 } 33 if((i&1)&&h2<=t2&&s[q2[h2]]<=s[i]){ 34 ans1=S[i]-S[q2[h2]],ans2=i-q2[h2],g=gcd(ans1,ans2),ans1/=g,ans2/=g;return 1; 35 } 36 if(!((i-L+1)&1)){ 37 while(h1<=t1&&s[q1[t1]]>=s[i-L+1]) --t1; 38 q1[++t1]=i-L+1; 39 }else{ 40 while(h2<=t2&&s[q2[t2]]>=s[i-L+1]) --t2; 41 q2[++t2]=i-L+1; 42 } 43 } 44 return 0; 45 } 46 int main(){ 47 // freopen("testdata.in","r",stdin); 48 n=read(),L=read(),R=read(),m=n<<1; 49 double l=1<<30,r=0; 50 for(int i=1;i<=n;++i) A[i]=A[i+n]=read(),cmin(l,(double)A[i]),cmax(r,(double)A[i]); 51 for(int i=1;i<=m;i++) S[i]=S[i-1]+A[i]; 52 for(int i=1;i<=50;++i){ 53 double mid=(l+r)/2; 54 check(mid)?l=mid:r=mid; 55 } 56 printf("%lld/%lld",ans1,ans2); 57 return 0; 58 }