【题解】Kathy函数 [BZOJ1223] [P2235] [HNOI2002]

【题解】Kathy函数 [BZOJ1223] [P2235] [HNOI2002]


这几疯狂刷了数位\(dp\)的题,到这道题时被卡了一天,一看大佬的讲解发现居然是求回文数╮(╯_╰)╭
感觉被大佬狠狠地蹂蹑了一番。。。

废话到此结束,进入正题:

满足\(f(n)=n\)\(n\)在二进制的形式下一定是一个回文数

例如:

\(f(1)=1\) (\(1\))
\(f(3)=3\) (\(11\))
\(f(5)=5\) (\(101\))
\(f(7)=7\) (\(111\))

至于为什么会有这个性质,这里就作证明(都懒得给自己的懒惰找借口了)

假设已经成功的证明了这个结论,那么问题就变成了在\([1,n]\)中找到符合二进制形式为回文数的总个数,这里我提供一种用\(dfs\)实现数位\(dp\)的思路

可以先写一下这道题(因为不需要高精):
只需要输出区间内回文数的总个数

LightOJ1205


\(dfs\)找回文数个数:

一:转进制

首先我们需要把\(n\)转化为二进制存储在一个数组\(num[lenn]\)

二:DFS

\(dfs(st,pos,ok,limit)\)

记搜以及状态表示

用一个数组记录已经搜过的地方,\(dp[pos][ok][st]\) 示已经选到了第\(pos\)位,前已经选好的是否满足回文\(ok\),去前导\(0\)后共有\(st\)位,并且前面已选的并未全部达到上限(\(pos\)开始后面可以随意选\(0\)\(1\)),此时的回文数总数。

\(limit\)表示前面已选的是否全达到上限,如果是,那么这一位的可以选的数的上限为\(num[pos]\),否则可以随意选,\(ed=limit?num[pos]:1\)

如果当前状态并未受限,且当前状态的\(dp\)值已经被记录,直接返回这个值

如何枚举递归

每当选了一个数\(i\)时, \(tmp[pos]=i\)

(1) 如果前面都没选(即全是前\(0\)\(st=pos\)),
\(ans+=dfs\)(\(st-1\),\(pos-1\),\(ok\),\(limit\)&&\(i==ed\))

(2) 如果前面选了(即\(st>pos\)

  1. 如果当前状态是回文数,且当前选的这位在\(st\)的后半段(即\(ok\)&&\(pos<=st/2\)),则\(okk=tmp[st-pos+1]==i\)
  2. 否则 \(okk=ok\)
    \(ans+=dfs\)(\(st\),\(pos-1\),\(okk\),\(limit\)&&\(i==ed\))

\(dfs\)边界

\(pos==0\)(即已经选完了所有位)
如果满足回文数(即\(ok=1\))且选了数(即并非全是\(0\)\(st>0\)\(return 1\)

三.高精

推荐写结构体,用起来方便,不会的可以参考一下大佬的高精模板

最后附上代码:

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int base = 1e8;
const int N = 20;
int aux[N << 3];
struct bigint {
    int s[N], l;
    void CL() { l = 0; memset(s, 0, sizeof(s)); }
    void pr()
    {
        printf("%d", s[l]);
        for (int i = l - 1; i; i--)
            printf("%08d", s[i]);
    }
    void re_l()
    {
        int i, x = 0, k = 1, L = 0, fl, o;
        char c = getchar();
        for (; c < '0' || c > '9'; c = getchar());
        for (; c >= '0' && c <= '9'; c = getchar())
        {
            if (!(L - 1) && !aux[L])
                L--;
            aux[++L] = c - '0';
        }
        CL();
        l = L / 8 + ((o = L % 8) > 0);
        for (i = 1; i <= o; i++)
            x = x * 10 + aux[i];
        if (o)
            s[l] = x;
        fl = !o ? l + 1 : l;
        for (i = o + 1, x = 0; i <= L; i++, k++)
        {
            x = x * 10 + aux[i];
            if (!(k ^ 8))
            {
                s[--fl] = x;
                x = k = 0;
            }
        }
        if (!l)
            l = 1;
    }
    ll toint()
    {
        ll x = 0;
        for (int i = l; i; i--)
            x = x * base + s[i];
        return x;
    }
    bigint operator = (int b)
    {
        CL();
        do
        {
            s[++l] = b % base;
            b /= base;
        } while (b > 0);
        return *this;
    }
    bigint operator = (ll b)
    {
        CL();
        do
        {
            s[++l] = b % base;
            b /= base;
        } while (b > 0);
        return *this;
    }
    bigint operator + (const int &b)
    {
        bigint c = *this;
        ll x = b;
        for (int i = 1; i <= l && x; i++)
        {
            x = x + c.s[i];
            c.s[i] = x % base;
            x /= base;
        }
        if(x)c.s[++c.l] = x;
        return c;
    }
    bigint operator + (const ll &b)
    {
        bigint c = *this;
        ll x = b;
        for (int i = 1; i <= l && x; i++)
        {
            x = x + c.s[i];
            c.s[i] = x % base;
            x /= base;
        }
        if (x)
            c.s[++c.l] = x;
        return c;
    }
    bigint operator + (bigint &b)
    {
        if (b.l < 3)
            return *this + b.toint();
        bigint c;
        ll x = 0;
        int k = l < b.l ? b.l : l;
        c.CL();
        c.l = k;
        for (int i = 1; i <= k; i++)
        {
            x = x + s[i] + b.s[i];
            c.s[i] = x % base;
            x /= base;
        }
        if (x)
            c.s[++c.l] = x;
        return c;
    }
    bigint operator / (const int &b)
    {
        bigint c;
        ll x = 0;
        c.CL();
        for (int i = l; i; i--)
        {
            c.s[i] = (x * base + s[i]) / b;
            x = (x * base + s[i]) % b;
        }
        for (c.l = l; !c.s[c.l] && c.l > 1; c.l--);
        return c;
    }
    bigint operator % (const int &b)
    {
        bigint c;
        ll x = 0;
        c.CL();
        for (int i = l; i; i--)
            x = (x * base + s[i]) % b;
        return c = x;
    }
    bool operator > (const bigint &b) const
    {
        if (l ^ b.l)
            return l > b.l;
        for (int i = l; i; i--)
            if (s[i] ^ b.s[i])
                return s[i] > b.s[i];
        return false;
    }
    bigint operator += (bigint &b)
    {
        return *this = *this + b;
    }
    bool operator > (int b) const{
        bigint c;return *this > (c = b);
    }
};
//以上皆为高精度部分

bigint a;
int num[350],tmp[350];bigint dp[350][2][350];
bool pan[350][2][350];
inline bigint dfs(int st,int len,int ok,bool limit){
    bigint ans,pp;ans=0;pp=0;
	if(len<1){//边界 
    	if(ok&&st>0)ans=1;
    	return ans;
    }
    if(!limit&&pan[len][ok][st])return dp[len][ok][st];//返回所记录的值 
    int ed=limit?num[len]:1;//处理枚举上限 
    for(int i=0;i<=ed;i++){
        tmp[len]=i;//记录每次选的数,方便判断是否满足回文 
        if(st==len&&!i)pp=dfs(st-1,len-1,ok,limit&&i==ed),ans+=pp;//如果一直都没选数 
        else pp=dfs(st,len-1,(ok&&len<=st/2)?tmp[st-len+1]==i:ok,limit&&i==ed),ans+=pp;//如果选了数 
	}
    if(!limit)dp[len][ok][st]=ans,pan[len][ok][st]=1;//记录 
    return ans;
}
int main(){
    a.re_l();ll Lenn=0;
    while(a>0){num[++Lenn]=(a%2).toint();a=(a/2);}//转成2进制存起来 
    bigint ans=dfs(Lenn,Lenn,1,1);
    ans.pr();
    return 0;
}
posted @ 2019-02-19 10:40  辰星凌  阅读(412)  评论(1编辑  收藏  举报