cppAcademy
FROM LEARNING TO COMPETING

Expert.
Put your skills to the test.

Bulgarian Informatics Group A · 2009–2026

217 independently explained problems: 54 Municipal, 54 Regional and 109 National. Start with the easier tasks and work upwards. Difficulty labels are learning estimates, not official ratings; Group A also includes challenging national-level problems. The 109 National-round tasks shared by Groups A and B retain their existing permanent URLs and explanations. Years follow Arena’s competition-season labels. Source clarifications and special grader requirements are noted on affected tasks.
GROUP A2016 · HardGeometry optimization

Maximum Rotating Rectangle Shadow

National Olympiad — Municipal round (2016 season)

Submission name: shadow_new.cpp

Problem summary

Two identical a by b rectangular sheets rotate about their common center in a horizontal plane. Find the maximum union area, which equals their shadow area under vertical sunlight.

Input

Positive integers a b.

Output

Maximum area rounded to exactly three digits after the decimal point.

Constraints

1 ≤ a,b ≤ 10000.

Worked example

Official sample; other valid outputs may exist.

Input

11 9

Output

117.258

Solution analysis

  1. Assume a≤b and let θ be the relative angle in [0,π/2]. At θ=π/2 the union area is 2ab−a². For θ≥2 arctan(a/b), the intersection is a parallelogram of area a²/sin θ, so the union increases toward that endpoint. It remains to optimize the earlier angles.
  2. Set t=tan(θ/2), so 0≤t≤a/b. Removing the two pairs of corner triangles from one rectangle shows that the union is U(t)=ab+t((b−at)²+(a−bt)²)/(2(1−t²)). For a=b,t=1 use its limiting value ab. The numerator of U′, apart from a positive factor, is H(t)=(a²+b²)(1+4t²−t⁴)−8abt.
  3. To find every stationary point, first split H at its unique minimum on [0,1]. Its derivative is 4(a²+b²)(2t−t³)−8ab; its first zero lies in [0,sqrt(2/3)], where it increases strictly. After that zero H increases through t=1. Bisect H on each resulting monotone interval within [0,a/b], evaluate all its roots and interval endpoints, and compare with 2ab−a². For 9 by 11 rectangles this gives 117.258.

Why it works

The intersection geometry has exactly the corner-triangle and parallelogram regimes described. Summing the four removed triangle areas gives U. Differentiation gives H. Since H′ increases up to sqrt(2/3), then decreases to H′(1)=4(a−b)²≥0, it has exactly one interior sign change, so H decreases and then increases. The two bisection intervals therefore contain every zero of U′. A continuous function on the compact angle range achieves its maximum at an endpoint or stationary point. Evaluating these candidates and the best parallelogram endpoint yields the global maximum.

Complexity: O(I) time and O(1) space, with I=100 bisection iterations per root.

Watch out. A right-angle crossing is not always optimal. A single ternary search across all angles is not justified. Use long double and evaluate the square endpoint by its limit.

Commented C++17 solution

An independently written complete program with standard headers.

#include<iostream>
#include<iomanip>
#include<cmath>
#include<algorithm>
using namespace std;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    long double a,b;
    cin>>a>>b;
    if(a>b){
        swap(a,b);
    }
    long double sum=a*a+b*b;
    long double product=a*b;
    auto h=[&](long double t){
        return sum*(1+4*t*t-t*t*t*t)-8*product*t;
    };
    auto derivative=[&](long double t){
        return 4*sum*(2*t-t*t*t)-8*product;
    };
    auto area=[&](long double t){
        if(t>=1){
            return product;
        }
        long double x=b-a*t;
        long double y=a-b*t;
        return product+t*(x*x+y*y)/(2*(1-t*t));
    };
    long double left=0,right=sqrtl(2.0L/3);
    // Find the unique minimum of H in [0,1].
    for(int iteration=0;iteration<100;++iteration){
        long double mid=(left+right)/2;
        if(derivative(mid)<0){
            left=mid;
        }
        else{
            right=mid;
        }
    }
    long double limit=a/b;
    long double split=min(limit,(left+right)/2);
    long double answer=max(2*product-a*a,area(limit));
    answer=max(answer,area(split));
    long double endpoints[3]={0,split,limit};
    for(int interval=0;interval<2;++interval){
        left=endpoints[interval];
        right=endpoints[interval+1];
        bool leftPositive=h(left)>0;
        if(leftPositive==(h(right)>0)){
            continue;
        }
        for(int iteration=0;iteration<100;++iteration){
            long double mid=(left+right)/2;
            if((h(mid)>0)==leftPositive){
                left=mid;
            }
            else{
                right=mid;
            }
        }
        answer=max(answer,area((left+right)/2));
    }
    cout<<fixed<<setprecision(3)<<answer<<'\n';
}

Source: Arena Olimpiici ↗