【JZOJ3771】【NOI2015模拟8.15】小 Z 的烦恼

Description

小 Z 最近遇上了大麻烦,他的数学分析挂科了。于是他只好找数分老师求情。
善良的数分老师答应不挂他,但是要求小 Z 帮助他一起解决一个难题问题是这样的,现在有 n 个标号为 1~n 的球和 m 个盒子,每个球都可以放进且只能放进一个盒子里面,但是要满足如下的规则:
1. 若把标号为 i 的球放进了第 j 个盒子,那么标号为 2*i 的球一定要在第 j+1 个盒子里面(若 j< m)
2. 若把标号为 i 的球放进了第 j 个盒子,并且 k*2=i,那么标号为 k 的球一定要在第 j-1 个盒子里面(若 j>1)
小 Z 的数分老师想要知道,给定了 n 和 m 的时候,第一个盒子最多能放进去多少个球。事实上,他已经推算出了公式,但是需要检验当 n 趋向于无穷大时是否仍然满足这个公式,因此 n 可能会非常大。

Data Constraint

对于 10%的数据,n<=10^6
对于 20%的数据,n<=10^9
对于 30%的数据,m=2
对于 100%的数据,n<=10^10000,2<=m<=25

Solution

我们能放在第一列的数一定满足 a2x(x0(modm),a1(mod2),a2x+m1<=n) 的形式,所以我们考虑从0开始枚举x,先将n除去 2m1 ,每次统计1~n里面的奇数,然后将n除去 2m 即可,由于n很大,所以每次要高精度除法。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll maxn=1e4+5,mo=1e11;
ll a[maxn],ans[maxn],b[maxn];
ll n,i,t,j,k,l,x,y,z,m,test,num;
char s[maxn];
void make(ll x){
    ll i,j,k,t=0;
    for (i=a[0];i>=1;i--){
        a[i]=t*mo+a[i];
        t=a[i]%x;a[i]/=x;
    }
    while (!a[a[0]] && a[0]) a[0]--;
}
void jia(){
    ll i,j,k,t=0;
    for (i=a[0];i>=1;i--){
        ans[i]+=(t*mo+a[i])/2;
        t=a[i]%2;
    }
    t=max(a[0]-1,ans[0]);
    if (ans[t+1]) t++;
    for (i=1;i<=t;i++)
        if (ans[i]>=mo) ans[i]-=mo,ans[i+1]+=1;
    if (ans[i]>0) ans[0]=i;
    else ans[0]=i-1;
}
int main(){
//  freopen("data.in","r",stdin);freopen("data.out","w",stdout);
    scanf("%d\n",&test);
    while (test){
        memset(ans,0,sizeof(ans));
        memset(a,0,sizeof(a));
        scanf("%s%d\n",s+1,&m);n=strlen(s+1);t=1;k=1;
        for (i=n;i>=1;i--){
            a[t]+=(s[i]-48)*k;k*=10;
            if (k==mo) t++,k=1;
        }
        a[0]=t;
        make(1<<(m-1));
        t=a[1]%2;
        jia();
        ans[1]+=t;
        while (a[0]){
            make(1<<(m));
            t=a[1]%2;
            jia();
            ans[1]+=t;
        }
        if (!ans[0]) ans[0]=1;
        printf("%lld",ans[ans[0]]);
        for (i=ans[0]-1;i>=1;i--){
            k=mo/10;
            while (ans[i]<k && k>0)printf("0"),k/=10;
            printf("%lld",ans[i]);
        }
        printf("\n");
        test--;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值