UVALive - 7271 A Math Problem (hihocoder 1259)

vj链接

hihocoder链接

这题很多人在vj上提交没过,原因是uvalive的数据有问题T_T,提交请到hihocoder。

这个题目倒是不难。

          题意:定义了f(n)序列和g(n)序列。其中g(t)表示[1,n]内满足f(i)%p=t的i的数量,其中t取值是[0,p-1],输出所有g(i)的异或值。

题解:p的值就是特定的几个,所以求出每个g(i)然后异或。现在问题就是就一个g(i),先要看f(n)的规律:

题目给的条件:


          从等式开始:左边3*f(n)和右边1+3*f(n)很显眼,两个相邻数的gcd等于1。那么就简单,这是等式,所以f(2n)一定是3*f(n)的倍数,结合那个不等式,倍数只能是1,所以f(2n)=3*f(n),带到原等式还能得到f(2n+1)=3*f(n)+1;

根据这两个等式求解f(n).其实仔细看这两个等式可以猜出结论,f(n)总是等于f(n/2)的3倍然后加上0或1,显然n是奇数就加1,这就猜出和2进制有关,n展开成二进制,然后倍数是3,那么数位的权值就是3,也就是3进制。

具体的:算f(5),5的二进制:101,然后权值是3,就把101当成3进制算:1*3^2+1*3^0=10;所以f(5)=10;

上述是靠感觉。若要追根求源,参看《具体数学》第一章第三节“约瑟夫问题”中最后得出求解一般性封闭式递归的公式,套用公式能得出相同的结论。

          最后一个问题就是算g(t),典型的数位dp,求解区间[1,n]内满足一个f(i)变换的取模计数问题(不是很懂的想一下,然后不会数位dp的请戳这里)。其实就是二进制枚举,权值按3算,边算边取模,最后等于t就是统计。当然这题蛮卡时间,所以优化,第一就是用t去减枚举的数,最后判0,第二就是dp开到三维,保存不同模下的dp状态。这一维只需要5,因为模指定的,这样memset就可以在多组数据外(这些优化不会的还是戳上面一个链接)。

代码:

#pragma comment(linker, "/STACK:10240000,10240000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<set>
#include<vector>
#include<map>
#include<stack>
#include<cmath>
#include<algorithm>
using namespace std;
const double R=0.5772156649015328606065120900;
const int N=1e2+5;
const int INF=0x3f3f3f3f;
const double eps=1e-8;
const double pi=acos(-1.0);
typedef long long ll;
ll dp[5][66][65540];
//dp[i][j][k]表示第i个模下,第j个数位,所减剩的模值为k
int mod,wh;
int a[66];
int resolve(ll n)
{
    int pos=0;
    while(n)
    {
        a[pos++]=n&1;
        n>>=1;
    }
    return pos;
}
ll p[100];
void init(int n)
{
    p[0]=1;
    for(int i=1;i<=n;i++)
        p[i]=p[i-1]*3%mod;//三进制权值
}
ll dfs(int pos,int val,bool limit)
{
    if(pos==-1) {
        return val==0;
    }
    if(!limit && dp[wh][pos][val]!=-1) return dp[wh][pos][val];
    int up=limit?a[pos]:1;
    ll ans=0;
    for(int i=0;i<=up;i++)
    {
        int v=val;
        if(i==1) {v-=p[pos];v=(v%mod+mod)%mod;}//数位只有0,1,权值p[pos]
        ans+=dfs(pos-1,v,limit && i==a[pos]);
    }
    if(!limit) dp[wh][pos][val]=ans;
    return ans;
}
int main()
{
    ll n;
    int T_T;
    scanf("%d",&T_T);
    memset(dp,-1,sizeof dp);//优化
    while(T_T--)
    {
        scanf("%lld%d",&n,&mod);
        if(mod==3) wh=0;//不同的模,相当于hash
        else if(mod==5) wh=1;
        else if(mod==17) wh=2;
        else if(mod==257) wh=3;
        else wh=4;
        ll ans=0;
        int pos=resolve(n);
        init(pos);
        for(int i=0;i<mod;i++)
            ans^=dfs(pos-1,i,true)-(i==0);//i=0时,会把统计f(0)统计在内,所以减掉
        printf("%lld\n",ans);
    }
    return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值