直接上官网题解吧:http://blog.jisuanke.com/?p=146
题意即求
其中
且
因为 47 是质数,如果把ai, li, ri, xi 写作 47 进制数
因为 47 是质数,根据Lucas定理 ,题目转换为求
的值,同时满足:
– 字典序不小于 不大于
– x1 + x2 + x3 ≤ n.
形象地说,题目相当于要填一个 3 行 m 列的矩阵,每一个格子是一个 [0, 47) 的整数。我们使用动态规划解决这个问题,其中涉及基本的数位型动态规划和HNOI 2007《梦幻岛宝珠》一题中的技巧。
我们用 f(i,j, mask, r) 记录一个状态,表示从高位往低位填,现在要填第 i 行第 j 列这个格子,mask 是一个 6 位二进制数,分别表示每一行是否已经大于下界(小于上界),r 表示现在总量还剩余r × 47j。需要注意 r ≤ 3 × 47。转移比较简单,只要枚举这个格子填什么,对应转移即可。
我这里用的是容斥 一个边界的数位DP比两个边界的好打多了
然后么 一定要注意r<=3*47 不能是3*46 WA了好久
Lucas定理出数位DP题 新idea get!!
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(ll &x){
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
const int N=55;
const int P=47;
const int M=15;
int C[N][N];
void Pre(){
C[0][0]=1;
for (int i=1;i<=47;i++){
C[i][0]=1;
for (int j=1;j<=i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%P;
}
}
ll maxn;
ll L[5],R[5],A[5];
const int n=3;
int m;
int w[M],l[5][M],a[5][M];
int Mac(ll x,int *w,int len=0){
int tem=0;
while (x) w[++tem]=x%47,x/=47;
if (len)
while (tem<len) w[++tem]=0;
reverse(w+1,w+tem+1); return tem;
}
int f[5][M][2][2][2][47*3+5];
void add(int &x,int y){
x+=y; if (x>=P) x-=P;
}
int Solve(ll lim1,ll lim2,ll lim3){
cl(f);
Mac(lim1,l[1],m); Mac(lim2,l[2],m); Mac(lim3,l[3],m);
f[1][1][1][1][1][w[1]]=1;
int k[10],si,sj,val,tem; int ret=0;
for (int j=1;j<=m;j++){
for (int i=1;i<=3;i++){
for (int o=0;o<8;o++){
k[1]=o&1; k[2]=o>>1&1; k[3]=o>>2&1;
for (int r=0;r<=47*3;r++){
val=f[i][j][k[1]][k[2]][k[3]][r];
if (!val) continue;
if (i==3) {
si=1,sj=j+1;
if (k[i]==1){
tem=min(l[i][j],min(r,46));
for (int v=0;v<=tem;v++){
k[i]=v==l[i][j];
add(f[si][sj][k[1]][k[2]][k[3]][min(47*3,(r-v)*47+w[sj])],val*C[a[i][j]][v]%P);
}
k[i]=1;
}else{
tem=min(r,46);
for (int v=0;v<=tem;v++)
add(f[si][sj][k[1]][k[2]][k[3]][min(47*3,(r-v)*47+w[sj])],val*C[a[i][j]][v]%P);
}
}else{
si=i+1,sj=j;
if (k[i]==1){
tem=min(l[i][j],min(r,46));
for (int v=0;v<=tem;v++){
k[i]=v==l[i][j];
add(f[si][sj][k[1]][k[2]][k[3]][r-v],val*C[a[i][j]][v]%P);
}
k[i]=1;
}else{
tem=min(r,46);
for (int v=0;v<=tem;v++)
add(f[si][sj][k[1]][k[2]][k[3]][r-v],val*C[a[i][j]][v]%P);
}
}
}
}
}
}
for (k[1]=0;k[1]<2;k[1]++)
for (k[2]=0;k[2]<2;k[2]++)
for (k[3]=0;k[3]<2;k[3]++)
for (int r=0;r<=47*3;r++)
add(ret,f[1][m+1][k[1]][k[2]][k[3]][r]);
return ret;
}
int main(){
ll T; int Ans=0;
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(T); Pre();
while (T--){
for (int i=1;i<=n;i++) read(A[i]),read(L[i]),read(R[i]); read(maxn);
maxn=min(maxn,A[1]+A[2]+A[3]);
m=max(Mac(A[1],a[1]),max(Mac(A[2],a[2]),Mac(A[3],a[3])));
m++;
for (int i=1;i<=n;i++) Mac(A[i],a[i],m);
Mac(maxn,w,m);
Ans=0;
Ans+=Solve(R[1],R[2],R[3]); Ans=(Ans+P)%P;
Ans-=Solve(R[1],R[2],L[3]-1); Ans=(Ans+P)%P;
Ans-=Solve(R[1],L[2]-1,R[3]); Ans=(Ans+P)%P;
Ans-=Solve(L[1]-1,R[2],R[3]); Ans=(Ans+P)%P;
Ans+=Solve(L[1]-1,L[2]-1,R[3]); Ans=(Ans+P)%P;
Ans+=Solve(R[1],L[2]-1,L[3]-1); Ans=(Ans+P)%P;
Ans+=Solve(L[1]-1,R[2],L[3]-1); Ans=(Ans+P)%P;
Ans-=Solve(L[1]-1,L[2]-1,L[3]-1); Ans=(Ans+P)%P;
printf("%d\n",Ans);
}
return 0;
}