zoukankan      html  css  js  c++  java
  • 「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;
    }
  • 相关阅读:
    event对象之与onmouse相关的事件触发
    对文档树进行导航
    event对象的onkeydown使用
    event的onchange方法
    函数名-函数参数坑-迭代器
    函数进阶-名称空间
    初识函数
    文件管理
    基础数据类型补充-编码进阶
    集合-缓存机制-深浅copy
  • 原文地址:https://www.cnblogs.com/qixingzhi/p/10349661.html
Copyright © 2011-2022 走看看