【AtCoder Grand Contest 020 D】Min Max Repetition

贪心,构造。

题目大意:

多组询问,每次给定 $a,b,l,r$,要求用 $a$ 个 A 和 $b$ 个 B 组成字符串,满足:

  • 最长的连续相同字符段的长度最短。
  • 在此基础上字典序最小。

要求输出第 $l\sim r$ 个字符。

题解:

假设 $a\geq b$。

最短的最长的连续相同字符段长度为 $x$。

那么要满足 $(b+1)x\geq a$(每 $x$ 个 A 后跟一个 B,最后还可以跟 $x$ 个 A)。

可以得到 $x=\lceil\frac{a}{b+1}\rceil$.

然后考虑构造字典序最小的解。

要求字典序最小,我们显然要尽可能把 A 放前面。

那么我们的方案是,最前面放若干组 AA...AB(有 $x$ 个 A),中间放若干个 A,然后再放若干个 B,然后再放若干组 ABB...B(有 $x$ 个 B)。

最前面的组数,我们可以通过二分得到,最后的组数,可以通过余下的 AB 的个数算出来,然后中间就把剩余的插入即可。

对于 $a\lt b$ 的情况,相当于前面先放 AA...AB,后面放 ABB...B。实际上,把 $a$ 和 $b$ 交换后,按照 $a\geq b$ 的情况得到的串,把 AB 互换,再翻转,就是 $a\lt b$ 的情况。

Code:

#include<cstdio>
#include<stdint.h>
#define int long long
int T,a,b,L,R,x,l,r,ans;
inline bool check(int c){
    int aa=a-(c/(x+1)*x+c%(x+1)),bb=b-c/(x+1);
    return aa*x>=bb;
}
int32_t main(){
    for(scanf("%lld",&T);T--;){
        scanf("%lld%lld%lld%lld",&a,&b,&L,&R);
        x=((a>b?a:b)-1)/((a<b?a:b)+1)+1,l=1,r=(a+b)/(x+1),ans=0;
        while(l<=r){
            const int mid=l+r>>1;
            if(check(mid*(x+1)))l=(ans=mid)+1;else r=mid-1;
        }
        ans*=x+1;
        int aa=a-(ans/(x+1)*x+ans%(x+1)),bb=b-ans/(x+1);
        int exta=aa-bb/x,extb=bb%x;
        for(int i=L;i<=R;++i)
        if(i<=ans)putchar(i%(x+1)?'A':'B');else
        if(i<=ans+exta)putchar('A');else
        if(i<=ans+exta+extb)putchar('B');else
        putchar((i-ans-exta-extb)%(x+1)==1?'A':'B');
        putchar('\n');
    }
    return 0;
}