bzoj 4918: [Lydsy1706月赛]回文数对

链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4918

容易发现,合法的回文是根号级别的
因为你只用枚举前面一半
然后你发现,可以数位DP
先估算一下数位DP的复杂度,设为位数S
那么得到了 S × T × R S \times T\times\sqrt{R} S×T×R 的算法
发现过不去。。

仔细分析,发现这个根号没有用。。
直接数位DP就好了
记录一下 f i , j , t f , t f 1 f_{i,j,tf,tf1} fi,j,tf,tf1表示做到第 i i i位,结果的非 0 0 0位出现在哪里,是否顶边界的答案
然后当当前已确定的位(从非0位开始数),和后面的位组合起来就是要的回文串
也就是当前是一半,就直接算
如果都没有顶边界,那么明显,答案就是 2 i 2^i 2i,让一个串随便填,另外一个根据回文来填即可
如果有一个顶边界,可以暴力枚举这个顶边界第一位不顶边界的,然后计算
否则,因为两个都顶边界,我们可以知道回文的前半段是什么,然后也是暴力枚举第一位变成第一个或第二个情况的是哪一位,然后计算就可以了

建议把有一个顶边界的预处理一下

做法很简单,但是实现有点难度。。

注意细节

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL N=50;
LL T;
LL a[N],b[N],tot;
LL f[N][N][2][2];
LL h[N],h1[N];
LL Pow[N];
LL g[N];//之前异或出来的答案是什么 
LL dfs (LL now,LL lst,bool tf,bool tf1)//上一个出现的位置是什么 是否出现了不贴边界的位 
{
    if (now<0) return 1;
    LL lalal=0;
    LL t=lst-now;
    if (now+1==t||now+2==t)//直接对称 
    {
    //  printf("YES! %lld\n");
        if (tf&&tf1)    lalal=Pow[now+1];
        else if ((tf^tf1)==1)//有一个不用贴边界 
        {
            if (tf1==true) lalal=h[now];
            else lalal=h1[now];
            //lalal=h[now]+1;
        }
        else//都贴边界 
        {
            LL o=0;
            if (now+1==t)
            {
                for (LL i=now+1;i<=lst;i++) g[++o]=(a[i]^b[i]);
            }
            else
            {
                for (LL i=now+2;i<=lst;i++) g[++o]=(a[i]^b[i]);
            }
        /*  for (int i=1;i<=o;i++) printf("%lld",g[i]);
            printf("\n");*/
            bool ok=true;
            for (LL i=now;i>=0;i--)//从这一位开始尝试不贴边界 
            {
                LL id=(now-i+1);//对应什么
                //printf("YES:%lld %lld\n",i,id);
                if (a[i]==1&&b[i]==1)//都可以不贴边界
                {
                    if (g[id]==0)//两人都不贴边界
                        lalal=lalal+Pow[i];
                    else
                    {
                        if (i==0) lalal+=2;
                        else lalal=lalal+h[i-1]+h1[i-1];
                    }
                }
                else if ((a[i]^b[i])==1)//有一个是1 
                {
                    if (g[id]==0)//必须不贴边界了
                    {
                        if (a[i]==1) 
                        {
                            if (i>0) lalal+=h1[i-1];
                        }
                        if (b[i]==1) 
                        {
                            if (i>0) lalal+=h[i-1];
                        }
                        //lalal=lalal+Pow[now];
                    }
                }
                if (g[id]!=(a[i]^b[i])) {ok=false;break;}
            }
            lalal=lalal+ok;
        }
    }
    else
    {
        if (tf==true&&tf1==true)
        {
            if (lst==-1)
            {
                lalal=lalal+dfs(now-1,-1,tf,tf1)*2;//00 11
                lalal=lalal+dfs(now-1,now,tf,tf1)*2;//10 01
            }
            else    lalal=lalal+dfs(now-1,lst,tf,tf1)*4;
        }
        else if (tf==true)
        {
            for (LL i=0;i<=b[now];i++)
            {
                if (lst==-1)
                {
                    lalal=lalal+dfs(now-1,-1,tf,i<b[now]);//00 11
                    lalal=lalal+dfs(now-1,now,tf,i<b[now]);//10 01
                }
                else    lalal=lalal+dfs(now-1,lst,tf,i<b[now])*2;
            }
        }
        else if (tf1==true)
        {
            for (LL i=0;i<=a[now];i++)
            {
                if (lst==-1)
                {
                    lalal=lalal+dfs(now-1,-1,i<a[now],tf1);//00 11
                    lalal=lalal+dfs(now-1,now,i<a[now],tf1);//10 01
                }
                else    lalal=lalal+dfs(now-1,lst,i<a[now],tf1)*2;
            }
        }
        else
        {
            for (LL i=0;i<=a[now];i++)
                for (LL j=0;j<=b[now];j++)
                {
                    if (lst==-1)
                    {
                        if ((i^j)==0) lalal=lalal+dfs(now-1,-1,i<a[now],j<b[now]);
                        else lalal=lalal+dfs(now-1,now,i<a[now],j<b[now]);
                    }
                    else    lalal=lalal+dfs(now-1,lst,i<a[now],j<b[now]);
                }
        }
    }
    if (f[now][lst+1][tf][tf1]!=0) return f[now][lst+1][tf][tf1];
/*  printf("%lld %lld %d %d %lld\n",now,lst,tf,tf1,lalal);
    system("pause");*/
    f[now][lst+1][tf][tf1]=lalal;
    return lalal;
}
LL calc (LL L,LL R)
{
    if (L<0||R<0) return 0;
//  printf("calc:%lld %lld\n",L,R);
    memset(f,0,sizeof(f));
    LL now=0,now1=0;a[0]=0;b[0]=0;
    while (L>0)  {a[now++]=(L&1);L>>=1;}
    while (R>0)  {b[now1++]=(R&1);R>>=1;}
    tot=max(0LL,max(now-1,now1-1));
    h[0]=h1[0]=1;
    //printf("%lld\n",tot);
    for (int u=0;u<=tot;u++) 
    {
        if (u>0) h[u]=h[u-1],h1[u]=h1[u-1];
        if (a[u]==1) h[u]=h[u]+Pow[u];
        if (b[u]==1) h1[u]=h1[u]+Pow[u];
    }
    for (LL u=now;u<=tot;u++) a[u]=0;
    for (LL u=now1;u<=tot;u++) b[u]=0;
/*  for (LL u=tot;u>=0;u--) printf("%lld",a[u]);
    printf("\n");
    for (LL u=tot;u>=0;u--) printf("%lld",b[u]);
    printf("\n");*/
    LL t=dfs(tot,-1,false,false);
    //printf("%lld\n",t);
    return t;
}
int main()
{
    Pow[0]=1;for (LL u=1;u<50;u++) Pow[u]=Pow[u-1]*2;    
    //calc(3,8);
    scanf("%lld",&T);
    while (T--)
    {
        LL L,R;
        scanf("%lld%lld",&L,&R);
        printf("%lld\n",calc(R,R)-calc(L-1,R)-calc(L-1,R)+calc(L-1,L-1));
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值