由于序列要求是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。
- x>0,y=0,z=0. dx=N
- x>0,y>0,z=0. dx+(d+1)y=N
- x>0,y=0,z>0. dx+(d+2)z=N
- 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;
}