bzoj3316 JC loves Mkk题解
3316: JC loves Mkk
Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 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
3 1 2 4 5
Sample Output
7/2
HINT
1≤L≤R≤n≤10^5,0≤ai≤10^9,保证问题有解,数据随机生成
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 }