【數位DP】SPOJ10606Balanced Numbers

傳送門
題目大意:一個數被稱爲是平衡的數當且僅當對於所有出現過的數位,偶數出現奇數次,奇數出現偶數次。
給定AB ,請統計出[A,B] 內所有平衡的數的個數。

注意,這裏的偶數是指出現過的數,並且不能計算前導零。蒟蒻一開始理解成所有的偶數和奇數,被坑成狗QAQ

對於每一個數有三種狀態:
0 :這個數還木有出現過。
1 :這個數出現過奇數次。
2 :這個數出現過偶數次。
於是乎用一個三進制數來表示每一種狀態,然後直接轉移吧…

窩寫的是遞推,但有些人說遞推會T,不知道咋地反正窩交C++ (g++ 4.3.2)就是木有T。好像這題寫記憶化搜索會快一些QAQ

蒟蒻分析應該是SPOJ的時間限制太無語辣,0.1s 23333
而記憶化搜索會在處理第二個cal()會快一些,畢竟第一次已經算了一些辣。

#include <iostream>
#include <cstdio>
#include <cstring>
#define LL long long int
using namespace std;

int p[15], cnt[10], w[20], len;
LL f[30][60005][2];

bool check(int j)
{
    memset(cnt,0,sizeof cnt);
    for(int i=0;j;++i, j/=3)
        cnt[i]=j%3;
    for(int i=0;i<10;++i)
        if(cnt[i])
        {
            if(i&1)
            {
                if(cnt[i]&1)return 0;
            }
            else if(!(cnt[i]&1))return 0;
        }
    return 1;
}

LL cal(LL n)
{
    if(!n)return 0;
    memset(f,0,sizeof f);
    LL m=n;
    len=0;
    while(m){w[++len]=m%10;m/=10;}
    f[len][p[w[len]]][1]=1;
    for(int i=len;i>0;--i)f[i][0][0]=1;
    for(int i=w[len]-1;i>0;--i)
        ++f[len][p[i]][0];
    for(int i=len-1, s;i>0;--i)
    {
        for(int j=0;j<59049;++j)
        {
            if(!f[i+1][j][1]&&!f[i+1][j][0])continue;
            for(int k=0;k<=9;++k)
            {
                if(!j&&!k)continue;//特殊處理前導零
                s=j/p[k]%3+1;
                s=s&1;
                if(s==0)s=2;
                s=j-j/p[k]%3*p[k]+s*p[k];
                if(k==w[i])f[i][s][1]+=f[i+1][j][1];
                else if(k<w[i])f[i][s][0]+=f[i+1][j][1];
                f[i][s][0]+=f[i+1][j][0];
            }
        }
    }
    LL ans=0;
    for(int j=1;j<59049;++j)
        if(check(j))ans+=f[1][j][0]+f[1][j][1];
    return ans;
}

int main()
{
    p[0]=1;
    for(int i=1;i<=11;++i)p[i]=p[i-1]*3;
    int cas;
    LL a, b;
    scanf("%d",&cas);
    while(cas--)
    {
        scanf("%lld%lld",&a,&b);
        printf("%lld\n",cal(b)-cal(a-1));
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章