Problem C. Partioning Number Google APAC 2017 University Test Round E

由于序列要求是non-decreasing,给定一组bucket 中ball的数目,只会有一种组合。

因为任意两个bucket差<=2,如果最左边的bucket d个ball,剩下的只能是d+1 or d+2个ball。

假设d个ball的bucket有x个,d+1个ball的bucket有y个,d+2个ball的bucket有z个。那么有以下四种case。

  1.  x>0,y=0,z=0. dx=N
  2.  x>0,y>0,z=0. dx+(d+1)y=N
  3.  x>0,y=0,z>0. dx+(d+2)z=N
  4.  x>0,y>0,z>0. dx+(d+1)y+(d+2)z=N

然后我就想到了排列组合里隔板法求方程整数解个数的问题,然后发现隔板法只用于系数为1的情况,然后就懵逼了/(ㄒoㄒ)/~~

后来得知case 2,case 3可以用拓展欧几里得求得不定方程的根的个数。case 4直接枚举x的取值即可。因为要保证y>0,z>0,枚举x时要确保剩下的ball个数>d+1+d+2。

reference在此欧几里德与扩展欧几里德算法

假设已经找到了方程的一组整数解,那么通解表达式为。根据可以得出t的取值范围,每一个t对应一个解,因此可以得出方程解的个数。

要注意下t的边界为正or负时+1,-1的问题。

#include<iostream>
#include<stdio.h>
#include<cstdio>
#include<string>
#include<cmath>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<vector>
#include<queue>
#include<map>

using namespace std;

//2017 RoundE Problem C. Partioning Number

int T;
int D;
int N;
long long ans;
int gcd(int a,int b)
{
    if(b==0)
        return a;
    return
        gcd(b,a%b);
}
int exgcd(int a,int b,long long &x,long long &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    int r=exgcd(b,a%b,x,y);
    int t=x;
    x=y;
    y=t-a/b*y;
    return r;
}
bool linear_equation(int a,int b,int c,long long &x,long long &y)
{
    int d=exgcd(a,b,x,y);
    if(c%d)
        return false;
    int k=c/d;
    x*=k; y*=k;
    return true;
}
int solve(int d1,int d2,int c)
{
    long long p0=0;
    long long q0=0;
    int u=gcd(d1,d2);
    if(N%u!=0)
    {
        return 0;
    }
    if(linear_equation(d1,d2,u,p0,q0)==true)
    {
        long long p1=p0*c/u;//use int may lead to overflow for large data
        long long q1=q0*c/u;
        long long upper=floor(1.0*u*q1/d1);
        if((u*q1)%d1==0)
        {
            upper--;
        }
//        if(upper<0&&(u*q1)%d1!=0)
//        {
//            upper--;
//        }
        long long lower=ceil(-p1*u*1.0/d2);
        if((p1*u)%d2==0)
        {
            lower++;
        }
//        if(lower>=0&&(p1*u)%d2!=0)
//        {
//            lower++;
//        }
        //lower=-lower;
        //cout<<p0<<" "<<q0<<" "<<p1<<" "<<q1<<" "<<u<<" "<<lower<<" "<<upper<<endl;
        return max((long long)0,upper-lower+1);
    }
    return 0;
}
int main()
{
//    cout<<ceil(1)<<endl;
    freopen("C-small-practice.in","r",stdin);
    //freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    scanf("%d",&T);
    for(int ca=1;ca<=T;ca++)
    {
        scanf("%d %d",&N,&D);
        ans=0;
        for(int i=1;i*D<=N;i++)
        {
            int d=i*D;
//            cout<<d<<endl;
            //case 1: only x
            if(N%d==0)
            {
                ans++;
            }
//            cout<<"case1 "<<ans<<endl;
            //case 2: only x and y
            ans+=solve(d,d+1,N);
//            cout<<"case2 "<<ans<<endl;
            //case 3: only x and z
            ans+=solve(d,d+2,N);
//            cout<<"case3 "<<ans<<endl;
            //case 4: x, y and z all exist
            for(int x=d;x<=N-2*d-3;x+=d)
            {
                ans+=solve(d+1,d+2,N-x);
//                cout<<x<<" case4 "<<ans<<endl;
            }
        }
        printf("Case #%d: %lld\n",ca,ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值