「Algospot」龙曲线DRAGON

一道考验思维的好题,顺便总结求第k大问题的常规思路;

传送门:$>here<$

题意

给出初始串FX,每分形一次所有X替换为X+YF,所有Y替换为FX-Y。问$n$代字符串第$p$位起长度为$l$的串。

数据范围:$n \leq 50, p \leq 10^9, l \leq 50$

Solution

将求解一个串转化为求解第$k$个字符。这样的话只有求解$l$次字符就好了。

如果直接暴力去做,肯定从初始串开始暴力去一轮一轮的展开。而实际上并不需要展开每一个,因为只需要求一个字符。我只需要知道它的位置就可以了。

问题的转化

我们考虑找出在每一轮中,我们所要求的字符包含于那个字符中——去展开那个我们需要的字符。而如何找出它包含于哪个字符这个问题只与展开后的长度有关。

问题转化为求解一个字符展开若干轮之后的长度。这是个子结构,可以用$(s,n)$来表示。通过观察我们发现,有递推关系$(s,n)=2(s,n-1)+2$

透过题解看本质

求第k大的问题

很多题目会让我求第$k$个答案。例如求第$k$字典序的字符串;有时并不是单单排序能解决的,例如求字典序第$k$大的LIS;也可能像这道题一样,询问一个庞大答案中的某一截。

联系学过的内容

在我们学过的内容中也有许多要求第$k$大的——最显然的就是主席树了。当然还有普通平衡树求解第$k$大。

这二者都是通过比较左子树与$k$的大小来决策第$k$大的位置。也就是说,将求第$k$大的问题转化为了判定问题。往往(类似于二分答案)转化为判定问题会简单很多。

通过暴力考虑优化

求解第$k$大的暴力做法是全部求出来然后取第$k$大。那么我们可以思考,前面那些是否对第$k$个有意义,是否可以省略。跳过我们不需要求的,就好像剪枝一样

my code

注意$l$的做的时候可能太大了会爆。刚好题目要我们求的位置不超过10亿,因此长度对10亿取min即可。

/*By DennyQi 2018*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 10010;
const int MAXM = 20010;
const int INF = 0x3f3f3f3f;
inline int Max(const int a, const int b){ return (a > b) ? a : b; }
inline int Min(const int a, const int b){ return (a < b) ? a : b; }
inline int read(){
    int x = 0; int w = 1; register char c = getchar();
    for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
    if(c == '-') w = -1, c = getchar();
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
}
int T,n,p,len,l[70];
inline char Char(int n, int k, bool c){
    if(n == 0){
        if(c == 0) return 'X';
        else return 'Y';
    }
    if(c == 0){
        if(l[n-1] >= k) return Char(n-1,k,0);
        if(l[n-1]+1 == k) return '+';
        if(l[n-1]+1+l[n-1] >= k) return Char(n-1,k-(l[n-1]+1),1);
        if(l[n-1]*2+2 == k) return 'F';
    }
    else{
        if(k == 1) return 'F';
        if(l[n-1]+1 >= k) return Char(n-1,k-1,0);
        if(l[n-1]+2 == k) return '-';
        if(l[n-1]*2+2 >= k) return Char(n-1,k-(l[n-1]+2),1);
    }
}
int main(){
    l[0] = 1;
    for(int i = 1; i <= 50; ++i) l[i] = Min((l[i-1]<<1) + 2, 1000000100);
    T = read();
    while(T--){
        n = read(), p = read(), len = read();
        for(int i = 0; i < len; ++i){
            if(p+i==1) printf("F");
            else printf("%c",Char(n,p+i-1,0));
        }
        puts("");
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/qixingzhi/p/10349661.html