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;
}
posted @ 2020-03-03 09:40  gmh77  阅读(276)  评论(0编辑  收藏  举报