6476. 【GDOI2020模拟02.19】A(范德蒙恒等式)
题目描述
题解
镇♂男则反
容斥下界,上界开到大概505位,数位dp最终的和V
设边界(要大于边界)之和为S,那么答案为C(V-S-1,n-1)
根据范德蒙恒等式,C(n+m,k)=∑C(n,i)*C(m,k-i)
如果nm都是正数很好证明,把n+m分成n和m两部分,枚举n部分选择个数组合一下
这个式子其实可以拓展到负数,证明要用生成函数
关于n为负数的组合数:\(C(n,m)=n^{\underline{m}}/m!\),其实和正数时是一样的
(注意这只是为了计算范德蒙恒等式而扩展的,在一般情况下当n<m时结果0)
于是C(V-S-1,n-1)=∑C(V,i)*C(-S-1,n-1-i)
对于每个i维护合法的V(V>=S+n)的∑C(V,i),转移相当于求∑C(V+Dj,i),等于∑(∑C(V,j))*C(Dj,i-j)
瞎写的时间应该是O(2n*D2*n3),把组合数预处理即可变成O(2n*D*n^2)
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define mod 1000000007
#define Mod 1000000005
#define ll long long
#define N 505
#define file
using namespace std;
ll w[13],L[13][N+1],R[13][N+1],c[N+1],b[1401],f[N+1][12][2],g[N+1][501][12],ans,s,S;
int d[13],n,D,i,j,k,l,len;
bool bz[501];
char ch;
ll qpower(ll a,int b)
{
ll ans=1;
while (b)
{
if (b&1)
ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
void swap(ll &x,ll &y)
{
int z=x;x=y;y=z;
}
void turn(ll *a)
{
int i,j,k,l=0;
while (len)
{
b[0]=0;
fd(i,len,1)
b[i-1]+=(b[i]%D)*10,b[i]/=D;
a[++l]=b[0]/10;
while (len && !b[len]) --len;
}
}
void Read()
{
int i,j,k,l;
len=0;
ch=getchar();
while (ch<'0' || ch>'9') ch=getchar();
while (ch>='0' && ch<='9') b[++len]=ch-'0',ch=getchar();
fd(i,len/2,1) swap(b[i],b[len-i+1]);
}
void add(ll *a,ll *b)
{
int i;
fo(i,1,N)
{
a[i]+=b[i];
if (a[i]>=D)
a[i]-=D,++a[i+1];
}
}
void dec(ll *a)
{
int i;
--a[1];
fo(i,1,N)
if (a[i]<0)
a[i]+=D,--a[i+1];
else
break;
}
ll MOD(ll *a)
{
ll ans=0;
int i;
fd(i,N,1)
ans=(ans*D+a[i])%mod;
return ans;
}
ll C(ll n,int m)
{
ll ans=1;
int i;
fo(i,1,m) ans=ans*(n-i+1)%mod*w[i]%mod;
return ans;
}
void dp(int type)
{
int i,j,k,l,I,J,K,L,s1,s2;
ll s,S;
fd(i,N,1)
{
if (c[i] && bz[c[i]])
{
fo(k,0,n-1)
f[i][k][0]=(f[i][k][0]+(g[i][c[i]][k]-g[i][c[i]-1][k]))%mod;
}
fo(j,0,n-1)
f[i][j][1]=(f[i][j][1]+(g[i][D-1][j]-g[i][c[i]][j]))%mod;
if (c[i]) break;
}
fd(i,N,2)
{
I=i-1;
fo(j,0,n-1)
{
J=j;
fo(l,0,j)
{
if (c[I])
f[I][J][0]=(f[I][J][0]+f[i][l][0]*(g[I][c[I]][j-l]-g[I][c[I]-1][j-l]))%mod;
else
f[I][J][0]=(f[I][J][0]+f[i][l][0]*g[I][c[I]][j-l])%mod;
f[I][J][1]=(f[I][J][1]+f[i][l][0]*(g[I][D-1][j-l]-g[I][c[I]][j-l]))%mod;
f[I][J][1]=(f[I][J][1]+f[i][l][1]*g[I][D-1][j-l])%mod;
}
}
}
s=-MOD(c)-1+n;
fo(j,0,n-1)
ans=(ans+(f[1][j][0]+f[1][j][1])*C(s,n-1-j)*type)%mod;
}
void dg(int t,int s)
{
int i;
if (t>n)
{
memset(c,0,sizeof(c));
memset(f,0,sizeof(f));
c[1]=n;
fo(i,1,N) if (c[i]>=D) c[i+1]=c[i]/D,c[i]%=D; else break;
fo(i,1,n) if (!d[i]) add(c,L[i]); else add(c,R[i]);
dp(s);
return;
}
d[t]=0,dg(t+1,s);
d[t]=1,dg(t+1,-s);
}
int main()
{
freopen("A.in","r",stdin);
#ifdef file
freopen("A.out","w",stdout);
#endif
scanf("%d%d",&n,&D);
fo(i,0,D-1)
scanf("%d",&j),bz[i]=j;
fo(i,1,n)
{
w[i]=qpower(i,Mod);
Read(),turn(L[i]),dec(L[i]);
Read(),turn(R[i]);
}
S=1;
fo(i,1,N)
{
s=0;
g[i][0][0]=bz[0];
fo(j,1,D-1)
{
fo(k,0,n-1) g[i][j][k]=g[i][j-1][k];
s=(s+S)%mod;
if (bz[j])
{
fo(k,0,n-1)
g[i][j][k]=(g[i][j][k]+C(s,k))%mod;
}
}
S=S*D%mod;
}
dg(1,1);
printf("%lld",(ans+mod)%mod);
fclose(stdin);
fclose(stdout);
return 0;
}