hihoCode 1249 A Math Problem ACM/ICPC 2015 Beijing (数位dp+规律)

来源:互联网 发布:手机版电路设计 软件 编辑:程序博客网 时间:2024/06/01 23:02




        题目本身在UVa,但是据说数据有问题,请在hihoCoder上面提交……

        大致题意:告诉你一个整数函数满足的式子,然后f(1)~f(n)每个函数值对K取模,每一个统计取模之后每一个数字出现的次数,数尺所有数字出现次数的异或和。

        本来以为是一道找规律题或者说是数论题,但是没想到居然是一道dp?!根据题目给出的表达式,我们容易求出f(n)递推公式:f(2*n+1)=3*f(n)+1,f(2*n)=3*f(n),f(1)=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……也就是说每一个数字对应的函数值,相当于其对应二进制表示的三进制的值。

        有了这个之后,统计函数值对K取模后的剩余系下每个数字出现的次数就是个典型的数位dp了。我们记录dp[x][len][r]在第x个模数的情况下,当前长度为len,目前对结果取模后的数字是r的时候的方案数,显然有转移方程:dp[x][len][r]=dp[x][len-1][r]+dp[x][len-1][(r-3^i+K)%K]。表示当前位置分别取0和1的时候的方案。具体见代码:

#include<bits/stdc++.h>#define LL long long#define N 100010using namespace std;LL dp[5][70][65540],p[70],n,K,m;int num[70],tot;LL dfs(int len,int r,bool lim){    if (len==0) return r==0;    if (!lim&&dp[m][len][r]!=-1) return dp[m][len][r];    int up=lim?num[len]:1; LL res=0;    for(int i=0;i<=up;i++)        res+=dfs(len-1,(r-(LL)i*p[len-1]%K+K)%K,i==up&&lim);    if (!lim) dp[m][len][r]=res;    return res;}LL calc(LL n){    tot=0;    LL x=n,res=0;    while(x)    {        num[++tot]=x&1LL;        x>>=1LL;    }    for(int i=0;i<K;i++)        res^=dfs(tot,i,1)-(i==0);//0的时候不用记录    return res;}void init(){    p[0]=1;    for(int i=1;i<70;i++)        p[i]=p[i-1]*3LL%K;}int main(){    int T_T; cin>>T_T;    memset(dp,-1,sizeof(dp));    while(T_T--)    {        scanf("%lld%lld",&n,&K);        init();        if (K==3) m=0;        if (K==5) m=1;        if (K==17) m=2;        if (K==257) m=3;        if (K==65537) m=4;        printf("%lld\n",calc(n));    }    return 0;}


原创粉丝点击