agc020_d Min Max Repetition
agc020_d Min Max Reptition
https://atcoder.jp/contests/agc020/tasks/agc020_d
定义 \(f(A,B)\) 为参数为两个正整数 \(A,B\) ,结果为一个字符串的函数.
\(f(A,B)\) 满足以下要求
- 长度为 \(A+B\)
- 有 \(A\) 个'A'和 \(B\) 个'B'
- 在满足1,2的条件下,最小化连续的'A'或'B'的长度
- 在满足1,2,3的条件下,最小化字典序
\(Q\) 组询问,每组询问给出 \(A,B,C,D\) ,求 \(f(A,B)[C \cdots D]\)
\(1 \le Q \le 10^3\)
\(1 \le A_i,B_i \le 5 \times 10^8\)
\(1 \le C_i \le D_i \le A_i+B_i\)
\(D_i-C_i+1 \le 100\)
Tutorial
设 \(K\) 为 \(f(A,B)\) 中最长相同字符连续段的长度.考虑求出 \(K\)
不妨设 \(A<B\) ,根据抽屉原理,将 \(B\) 个字符放入 \(A+1\) 个抽屉中.所以得到
也就是说,有
我们考虑贪心构造,即依次填入每个字符,满足
- 当前连续字符长度小于等于 \(K\)
- 存在填补后面的字符串的合法方案
发现在一段前缀中,第2个条件是没有限制的.所以这段前缀形如"AA...ABA...ABAA..."其中除了最后一段外其他部分的'A'的连续段的长度为 \(K\) .
对于剩下的部分,设还有 \(A'\) 个'A'和 \(B'\) 个'B',此时一定有 \(B' \le K(A'+1)\) .
那么我们可以贪心的先放置一段连续的'B',使得 \(B'=KA'\) ,之后的部分形如"ABB...BABB...B..."其中'B'的连续段长度均为 \(K\)
那么我们只需要知道第一部分的长度,就可以很容易得到整个串的形态, \(O(1)\) 的得知每个位置的字符了.
考虑二分第一部分的'A'的数量 \(Na\) ,则 \(Nb=\lfloor \dfrac {Na-1}K \rfloor\) ,那么就可以得到 \(A',B'\) 判断.
时间复杂度 \(O(Q(\log A+D_i-C_i+1)\)
Code
#include <algorithm>
#include <cstdio>
#include <iostream>
#define debug(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
inline char nc()
{
// return getchar();
static char buf[100000],*l=buf,*r=buf;
return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
template<class T> void read(T &x)
{
x=0; int f=1,ch=nc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=nc();}
while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=nc();}
x*=f;
}
typedef long long ll;
int A;
int B;
int C;
int D;
int K;
int Q;
bool check(int Na)
{
int Nb=Na==0?0:(Na-1)/K;
return (B-Nb)<=(ll)(A-Na+1)*K;
}
int main()
{
read(Q);
for(int kase=1;kase<=Q;++kase)
{
read(A),read(B),read(C),read(D);
K=(max(A,B)-1)/(min(A,B)+1)+1;
int l=0,r=A,Na=-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) Na=mid,l=mid+1;
else r=mid-1;
}
int Nb=Na==0?0:(Na-1)/K;
int t=(B-Nb)-(A-Na)*K;
for(int i=C;i<=D;++i)
{
int x=i;
if(x<=Na+Nb) putchar(x%(K+1)==0?'B':'A');
else
{
x-=Na+Nb;
if(x<=t) putchar('B');
else
{
x-=t;
putchar(x%(K+1)==1?'A':'B');
}
}
}
putchar('\n');
}
return 0;
}