1. 程式人生 > >UVALive 7271 A Math Problem 【數位dp計數】

UVALive 7271 A Math Problem 【數位dp計數】

資料問題:

這題UVA上的資料應該是有問題,沒人AC,可以在hihocode上提交,http://hihocoder.com/problemset/problem/1259?sid=949910

解題思路:

分析3 × f(n) × f(2n + 1) =f(2n) × (1 + 3f(n)), f(2n) < 6 × f(n)可以發現,3*f(n),3*f(n)+1互素。又有限制條件f(2n)<6*f(n),可以推出f(2n)=3*f(n),f(2n+1)=3*f(n)+1。寫出前幾項:

f(1)=1,f(2)=3,f(3)=4,f(4)=9,f(5)=10,f(6)=12,f(7)=13,f(8)=27,f(9)=28...

f(1) = f(0012)=1=0013,f(2)=f(0102)=3=0103,f(3)=f(011)=4=0113…可以發現f(n)的功能其實就是把對應的n的二進位制表示當作三進位制來算。發現這個關係後,可以用數位dp算出1-n範圍內mod K餘數為r的數的個數,dp[p][r]表示從第p位開始餘數為r的數的個數。轉移為dp[p][r]+=dp[p-1][r-i*3p]。

程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<map>
#include<string>
#include<queue>
#include<vector>
#include<list>
#include<bitset>
//#pragma comment(linker,"/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
ll n;
int K;
int dig[100];
int fac[100];
ll dp[65][65537];
ll dfs(int p,int r,int is)
{
    if(p==-1)
    {
        return r==0;
    }
    ll res=0;
    if(!is&&dp[p][r]!=-1) return dp[p][r];
    int up=is?dig[p]:1;
    for(int i=0;i<=up;i++)
    {
        res+=dfs(p-1,(r-i*fac[p]+K)%K,is==1&&i==up);
    }
    if(!is) dp[p][r]=res;
    return res;
}
void solve()
{
    memset(dp,-1,sizeof dp);
    long long tmp=n;
    int len=0;
    while(tmp)
    {
        dig[len++]=tmp%2;
        tmp/=2;
    }
    ll ans=0;
    for(int i=0;i<K;i++) ans^=dfs(len-1,i,1)-(i==0);
    printf("%lld\n",ans);
}
int main()
{
    int t;
    fac[0]=1;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld%d",&n,&K);
        for(int i=1;i<100;i++) fac[i]=fac[i-1]*3%K;
        solve();
    }
   return 0;
}