【省选模拟】这道题(数位DP)(容斥)(组合数学)

在这里插入图片描述
在这里插入图片描述

  • 神仙题
  • 枚举每一个的下界容斥,对 2 n 2^n 2n 种情况算方案数,令最后的数位 V V V,当前的下界加起来+1 为 S S S,那么贡献就是 ( V − S n − 1 ) \binom{V-S}{n-1} (n1VS),所以 w a y s = ∑ V ≥ S ( V − S n − 1 ) [ v 合 法 ] = ∑ V ≥ 0 ( V n − 1 ) [ V + S 合 法 ] ways=\sum_{V\ge S}\binom{V-S}{n-1}[v合法]=\sum_{V\ge 0}\binom{V}{n-1}[V+S合法] ways=VS(n1VS)[v]=V0(n1V)[V+S]
    我们对每一个 i ∈ [ 0 , n ) i\in[0,n) i[0,n) 求出 ∑ V ≥ 0 ( V + S n − 1 ) [ V + S 合 法 ] \sum_{V\ge 0}\binom{V+S}{n-1}[V+S合法] V0(n1V+S)[V+S],然后用范德蒙恒等式:
    ( V n − 1 ) = ∑ i = 0 n − 1 ( V + S i ) ( − S n − i − 1 ) \binom{V}{n-1}=\sum_{i=0}^{n-1}\binom{V+S}{i}\binom{-S}{n-i-1} (n1V)=i=0n1(iV+S)(ni1S)
    所以我们只需要算 ∑ V ≥ S ( V i ) [ V 合 法 ] \sum_{V\ge S}\binom{V}{i}[V合法] VS(iV)[V],考虑把 ( V i ) \binom{V}{i} (iV) 用范德蒙恒等式拆成每一位的贡献,就是 ( V i ) = ∑ ( a ∗ b c j ) ( V − a ∗ b c i − j ) \binom{V}{i}=\sum\binom{a*b^c}{j}\binom{V-a*b^c}{i-j} (iV)=(jabc)(ijVabc),这样就可以按每一位的贡献统计,然后就是按套路算 ≥ S \ge S S 的方案数,需要钦定当前位 > > > 原来的,那么我们每一位预处理预处理一个前后缀和就可以。
    预处理复杂度 O ( 50 0 2 ∗ n ) O(500^2*n) O(5002n),询问复杂度 O ( 2 n ∗ 500 ∗ n 2 ) O(2^n*500*n^2) O(2n500n2)
#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int Mod = 1e9 + 7;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
void Add(int &a, int b){ a = add(a, b); }
void Mul(int &a, int b){ a = mul(a, b); }
void Dec(int &a, int b){ a = dec(a, b); }
cs int N = 550, M = 15; 
int n, D, inv[M], C[N][N][M], pre[N][N][M], suf[N][N][M], good[N];
struct atom{
	vector<int> G;
	atom(){ G.clear(); }
	void input(){
		static char S[N*10]; scanf("%s",S); int len=strlen(S);
		for(int i=len-1; ~i; i--) G.push_back(S[i]-'0');
	}
	int operator % (cs int &b){ int as = 0; for(int i=(int)G.size()-1;~i;i--) as=(as*10+G[i])%b; return as; } 
	void operator /= (cs int &b){
		for(int i=(int)G.size()-1,x=0;~i;i--){
			int now=x*10+G[i]; x=now%b; G[i]=now/b;
		} while(G.size()&&G.back()==0) G.pop_back();
	}
	bool iszero(){ return G.empty(); }
};
struct num{
	vector<int>G;
	num(){ G.clear(); }
	void reset(){ G.clear(); }
	void input(){
		atom T; T.input();
		while(!T.iszero()) G.push_back(T%D), T/=D;
	}
	void minus(){
		--G[0]; 
		for(int i=0; i<(int)G.size()-1; i++){
			if(G[i]<0) G[i]+=D,--G[i+1];  
		} if(!G.back()) G.pop_back();
	}
	void inc(){
		++G[0]; G.push_back(0);
		for(int i=0; i<(int)G.size(); i++){
			if(G[i]>=D) G[i]-=D,++G[i+1];
		} if(!G.back()) G.pop_back();
	}
	num operator + (cs num &A) cs{
		num B; int deg=max(A.G.size(),G.size()); B.G.resize(deg);
		for(int i=0; i<deg; i++) B.G[i]=((int)G.size()>i?G[i]:0)+((int)A.G.size()>i?A.G[i]:0);
		B.G.push_back(0); for(int i=0; i<deg; i++) if(B.G[i]>=D) B.G[i]-=D, ++B.G[i+1];
		if(!B.G.back()) B.G.pop_back(); return B;
	}
	int fnd(int x){ return (int)G.size()>=x?G[x-1]:0; }
	int len(){ return G.size(); }
	int val(){ int as=0; for(int i=G.size()-1;~i;i--)as=add(mul(as,D),G[i]); return as; }
}; 
num L[M], R[M];
num dnw;
int work(){
	static int dp[2][M],nxt[2][M],coef[N];
	memset(coef,0,sizeof(coef));
	memset(nxt,0,sizeof(nxt));
	nxt[1][0]=1;
	for(int i=1; i<=510; i++){
		memcpy(dp,nxt,sizeof(dp));
		memset(nxt,0,sizeof(nxt));
		for(int x=0; x<n; x++) if(dp[0][x]||dp[1][x]){
			int trs=add(dp[0][x],dp[1][x]);
			int now=dnw.fnd(i);
			for(int y=0; x+y<n; y++){
				if(now>0) Add(nxt[0][x+y],mul(trs,pre[i][now-1][y]));
				if(now+1<D){
					Add(nxt[1][x+y],mul(trs,suf[i][now+1][y]));
					if(i>=dnw.len()) Add(coef[x+y],mul(trs,suf[i][now+1][y]));
				}
				Add(nxt[0][x+y],mul(dp[0][x],C[i][now][y]));
				Add(nxt[1][x+y],mul(dp[1][x],C[i][now][y]));
				if(i==dnw.len()) Add(coef[x+y],mul(dp[1][x],C[i][now][y]));
			}
		}
	}
	int v=dec(0,dnw.val()), as=0;
	for(int x=0,c=1; x<n; x++){
		if(x) c=mul(c,mul(dec(v,x-1),inv[x]));
		Add(as,mul(c,coef[n-1-x]));
	} return as;
}
int main(){
	scanf("%d%d",&n,&D); 
	for(int i=0; i<D; i++) scanf("%d",&good[i]);
	inv[0]=inv[1]=1;
	for(int i=2; i<=n; i++) inv[i]=mul(Mod-Mod/i,inv[Mod%i]);
	for(int i=0; i<n; i++) L[i].input(),R[i].input(),L[i].minus();
	for(int i=1,coe=1; i<=510; i++,Mul(coe,D)){
		for(int d=0; d<D; d++) if(good[d]){
			C[i][d][0]=1;
			for(int j=1; j<n; j++) 
			C[i][d][j]=mul(mul(C[i][d][j-1],dec(mul(coe,d),j-1)),inv[j]);
		}
		for(int d=0; d<D; d++) for(int j=0; j<n; j++)
		pre[i][d][j]=add(d?pre[i][d-1][j]:0, C[i][d][j]);
		for(int d=D-1; d>=0; d--) for(int j=0; j<n; j++)
		suf[i][d][j]=add(d==D-1?0:suf[i][d+1][j], C[i][d][j]);
	}
	int ans = 0;
	for(int S=0; S<(1<<n); S++){
		dnw.reset(); int t=0;
		for(int i=0; i<n; i++)
		if(S>>i&1) ++t,dnw=dnw+R[i];
		else dnw=dnw+L[i]; 
		dnw.inc();
		if(t&1) Dec(ans,work()); 
		else Add(ans,work());
	} cout<<ans; return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值