# 12602 - OuQ String
> author: tangerine1202, ryona, atsushi
###### tags: `csst`
---
## Brief
有以下三種解法
:::info
## Keyword List
英文 | 中文 | 補充或連結
--------|--------|--------
:::
## Solution 1
### 變數、函式定義
- `ll len[k]`:S~k~的字串長度。
- `void solve(ll l, ll r, int k)`
- `ls`、`rs`:將查詢區間從對齊S~k~開頭,改為對齊關注部分開頭,方便比對。(s意指shift)
- 功能:輸出S~k~[l~r]字串
### 想法:
- 先觀察S~k~ = "O" + S~k-1~ + "u" + S~k-1~ + "Q",k<=50。
S~50~的長度約等於2^50^(≒10^15^),而char陣列至多開到10^9^左右,故無法以陣列模擬。
- 考慮以遞迴處理。將S~k~拆成"O"、S~k-1~、"u"、S~k-1~、"Q",五個部分。
### `void solve(ll l, ll r, int k)`
- Basic step
- 當k==1時,直接輸出"OuQ"中被查詢的部分。
- Recursion step
- S~k-1~部分:如果查詢區間涵蓋S~k-1~,則維護左右界後遞迴S~k-1~。
- "O"、"u"、"Q"部分:如在查詢區間內,則直接輸出。
```c
#include <stdio.h>
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
const ll inf = 1e18;
ll len[60];
void solve(ll l, ll r, ll k)
{
if (k == 1) {
for (int i = l; i <= r; i++)
printf("%c", "OuQ"[i]);
return;
}
// O
ll ls = l, rs = r;
if (ls <= 0 && 0 <= rs)
printf("O");
// S_k-1
ls = l - 1;
rs = r - 1;
if (0 <= rs && ls <= len[k - 1] - 1)
solve(
max(ls, 0),
min(rs, len[k - 1] - 1),
k - 1);
// u
ls = l - 1 - len[k - 1];
rs = r - 1 - len[k - 1];
if (ls <= 0 && 0 <= rs)
printf("u");
// S_k-1
ls = l - 1 - len[k - 1] - 1;
rs = r - 1 - len[k - 1] - 1;
if (0 <= rs && ls <= len[k - 1] - 1)
solve(
max(ls, 0),
min(rs, len[k - 1] - 1),
k - 1);
// Q
ls = l - 1 - len[k - 1] - 1 - len[k - 1];
rs = r - 1 - len[k - 1] - 1 - len[k - 1];
if (ls <= 0 && 0 <= rs)
printf("Q");
}
int main()
{
int n = 1;
len[1] = 3;
// while( 2*len[n]+3 <= inf )
// 但因 2*len[n]+3 可能會溢位,故移項以防止溢位問題
while ((inf - 3) / 2 > len[n]) {
n++;
len[n] = len[n - 1] * 2 + 3;
}
ll l, r, k;
while (scanf("%lld%lld%lld", &k, &l, &r) != EOF) {
solve(l, r, k);
printf("\n");
}
return 0;
}
// By tangerine1202
```
## Solution 2
概念與解法一差不多,但可能有比較白話
由以下兩個步驟推廣。
-----處理k很小的時候:
依據題目,可以設計出
'O' + (k-1) + 'u' + (k-1) + 'Q'
的遞迴式,而且不必優化。
-----對於k比較大的想法:
tmp是指k級字串的第tmp個字元 ( 字串是想像的 ) 。
我們需要判斷tmp是否在左界和右界之間。
如果是,就輸出。
先一層一層進入遞迴,若是tmp + 下一層的字串長度
( len[k] )仍然比左界還要小,就直接讓tmp+=len[k],並且return回到上一層。
```c
#include <stdio.h>
int k;
long long l, r, tmp, len[53], r1;
void rec(int k){
if( tmp+len[k] <= l ){ //優化時間複雜度
tmp+=len[k];
return ;
}
else if(tmp <= r){ //如果tmp>r,直接return
if(tmp >= l && tmp <= r) printf("O");
tmp++;
if(k!=1 && tmp <= r) rec(k-1);
if(tmp >= l && tmp <= r) printf("u");
tmp++;
if(k!=1 && tmp <= r) rec(k-1);
if(tmp >= l && tmp <= r) printf("Q");
tmp++;
}//tmp在範圍內就print,k不等於1以及tmp在範圍內就遞迴
else return ;
}
//反正長度都固定,用陣列記下來可以省時間(dp)
long long lenk(int k){
if(len[k]) return len[k];
if(k==1) return len[1] = 3;
return len[k] = 3 + 2*lenk(k-1);
}
int main(){
lenk(50);
while( scanf("%d %lld %lld", &k, &l, &r)!=EOF ){
tmp = 0;
rec(k);
printf("\n");
}
return 0;
}
// By ryona
```
## Solution 3
```c
#include <stdio.h>
long long length[53],l , r;;
int k;
char s1[5] = "OuQ";
char askingchar(int linex, long long chdex){ // linex 是等同於k
// chdex 等同於k行的幾個字(0開始)
if(linex == 1) // 遞迴到1
return s1[chdex];
if(chdex == 0) // 剛好要第一個字,一定是O
return 'O';
else if (chdex == length[linex]-1) // 剛好要最後一個字,一定是Q
return 'Q';
else if (chdex == length[linex-1]+1) // 剛好要中間字,一定是u
return 'u';
else
{
if(chdex < length[linex-1]+1)
return askingchar(linex-1,chdex-1); // 將chdex 換算成上一行的第幾個字
else
return askingchar(linex-1,chdex-length[linex-1]-2); // 同理,換算
}
}
int main(){
length[1] = 3;
for(int i=2;i<=53;i++){
length[i] = 2 * length[i-1] + 3;
}
while(~scanf("%d %lld %lld", &k, &l, &r)){
for(int i=l;i<=r;i++){
printf("%c",askingchar(k,i));
}
printf("\n");
}
return 0;
}
// By atsushi
```
## Solution 4
```c
#include <stdio.h>
long long k, l, r, len;
char search(long long n, long long len, long long k);
int main() {
while (scanf("%lld %lld %lld", &k, &l, &r) == 3) {
len = 3 * (1ll << k) - 3; // count the length
for (long long i = l; i <= r; i++) printf("%c", search(i, len, k));
printf("\n");
}
}
char search(long long n, long long len, long long k) {
if (n < k) return 'O'; // 前k項必為'O'
if (n == (len-1) / 2) return 'u'; // 中間項必為'u'
if (n > len-1-k) return 'Q'; // 後k項必為'Q'
// 將字串拆成 O, sk-1, u, sk-1, Q 五個部分
len = (len-3) / 2;
if (n > len) return search(n-len-2, len, k-1); // 第2段sk-1
else return search(n-1, len, k-1); // 第1段sk-1
}
// By Utin
```
## Reference