zoukankan      html  css  js  c++  java
  • 6476. 【GDOI2020模拟02.19】A

    题目

    题目比较简洁,就不复述了。


    思考历程

    这让我联想到了不久之前在CF上做的一道题。
    但这两道题的差别是很大的,共同点并不是很多。
    直接套那题的方法在这题中也顶多拿个40分。
    考虑探索新大陆,然而没有成功……
    感觉这题的暴力不好打,最终也没有打部分分……
    (所以说我在这题上耗费了1h)


    正解

    正解的思路比较清奇。
    不用说也知道这题要容斥一下,我比赛时一直在想着保留上界,然而正解是保留下界。
    如果想到的话也挺显然的,首先用最终的值减去下界,接下来用生成函数去考虑,就相当于(frac{1}{(1-x)^n})。这个东西可以用隔板法搞出来。假如最终的总和为(V),下界和为(S),那么方案数就是(C_{V-S+n-1}^{n-1})
    我们要将每个合法的(V)的这个东西加起来。
    有条公式:(C_{n+m}^k=sum_{i=0}^kC_n^iC_m^{k-i})
    可以用组合意义证明。我们用这条式子来搞事情。
    假如知道了(V)的信息,现在要知道(V+aD^b)的信息
    (C_{V+aD^b-S+n-1}^{n-1}=sum_{i=0}^{n-1}C_{V-S+n-1}^iC_{aD^b}^{n-1-i})
    于是,只要我们知道(C_{V-S+n-1}^i)(i in [0,n))),就可以推出(V+aD^b)的信息了。右边的那个东西预处理即可。
    可以发现不同的(V)是可以加在一起转移的。
    发现了这个东西之后就可以DP:设(f_{i,j,0/1}),表示前(i)位,计算的是(C_{V-S+n-1}^{j})的和,后面的(0/1)表示前(i)位的(V)和下界总和(即(S))的大小关系。


    代码

    代码中(f)数组加了一维,表示有没有前导零。

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cassert>
    #define mo 1000000007
    #define LEN 520
    #define maxD 500
    #define N 12
    #define ll long long
    inline ll qpow(ll x,int y=mo-2){
    	ll res=1;
    	for (;y;y>>=1,x=x*x%mo)
    		if (y&1)
    			res=res*x%mo;
    	return res;
    }
    int n,D;
    ll fac[N],ifac[N];
    inline ll C(ll m,int n){
    	m%=mo,m+=(m<0?mo:0);
    	ll res=ifac[n];
    	for (int i=0;i<n;++i,m=(m-1+mo)%mo)
    		res=res*m%mo;
    	return res;
    }
    ll powd[LEN+1],g[LEN+1][maxD][N],sg[LEN+1][maxD][N];
    struct Bigint{
    	int k,v[LEN+1];
    	inline int getmo(){
    		int res=0;
    		for (int i=k;i>=0;--i)
    			res=((ll)res*D+v[i])%mo;
    		return res;
    	}
    } L[N],R[N];
    void operator*=(Bigint &a,int b){
    	for (int i=0;i<=a.k;++i)
    		a.v[i]*=b;
    	for (int i=0;i<=a.k;++i){
    		a.v[i+1]+=a.v[i]/D;
    		a.v[i]%=D;
    	}
    	if (a.v[a.k+1])
    		a.k++;
    }
    void operator+=(Bigint &a,int b){
    	a.v[0]+=b;
    	for (int i=0;a.v[i]>=D;++i)
    		a.v[i+1]++,a.v[i]-=D;
    	if (a.v[a.k+1])
    		a.k++;
    }
    void operator+=(Bigint &a,Bigint &b){
    	a.k=max(a.k,b.k);
    	for (int i=0;i<=a.k;++i){
    		a.v[i]+=b.v[i];
    		if (a.v[i]>=D)
    			a.v[i+1]++,a.v[i]-=D;
    	}
    	if (a.v[a.k+1])
    		a.k++;
    }
    void operator-=(Bigint &a,Bigint &b){
    	for (int i=0;i<=a.k;++i){
    		a.v[i]-=b.v[i];
    		if (a.v[i]<0)
    			a.v[i+1]--,a.v[i]+=D;
    	}
    	if (a.v[a.k]==0)
    		a.k--;
    }
    void input(Bigint &a){
    	char ch=getchar();
    	while (ch<'0' || ch>'9')
    		ch=getchar();
    	do{
    		a*=10,a+=ch-'0';
    		ch=getchar();
    	}
    	while ('0'<=ch && ch<='9');
    }
    bool T[maxD];
    ll ans;
    ll f[LEN+1][N][2][2];
    Bigint s;
    inline ll calc(){
    	memset(f[0],0,sizeof f[0]);
    	int smo=s.getmo();
    	for (int j=0,w=s.v[0];j<n;++j){
    		for (ll v=0;v<D;++v)
    			if (T[v]){
    				ll tmp=C(v-smo+n-1,j);
    				(f[0][j][v>=w][bool(v)]+=tmp)%=mo;
    			}
    	}
    	for (int i=0;i<LEN;++i){
    		int w=s.v[i+1];
    		memset(f[i+1],0,sizeof f[i+1]);
    		for (int j=0;j<n;++j)
    			for (int c=0;c<2;++c){
    				ll val=(f[i][j][c][1]+(T[0]?f[i][j][c][0]:0))%mo;
    				for (int k=j;k<n;++k){
    					if (w)
    						(f[i+1][k][0][1]+=val*sg[i+1][w-1][k-j])%=mo;
    					if (w && T[w])
    						(f[i+1][k][c][1]+=val*g[i+1][w][k-j])%=mo;
    					(f[i+1][k][1][1]+=val*(sg[i+1][D-1][k-j]-sg[i+1][w][k-j]+mo))%=mo;
    				}
    				(f[i+1][j][0<w?0:c][0]+=f[i][j][c][1]+f[i][j][c][0])%=mo;
    			}
    	}
    	ll res=f[LEN][n-1][1][1]+f[LEN][n-1][1][0];
    	return res%mo;
    }
    void dfs(int k,int flag){
    	if (k==n){
    		ans+=calc()*flag;
    		return;
    	}
    	s+=L[k];
    	dfs(k+1,flag);
    	s-=L[k];
    	s+=R[k];
    	dfs(k+1,-flag);
    	s-=R[k];
    }
    int main(){
    	freopen("A.in","r",stdin);
    	freopen("A.out","w",stdout);
    	scanf("%d%d",&n,&D);
    	for (int i=0;i<D;++i){
    		 int x;
    		 scanf("%d",&x);
    		 T[i]=x;
    	}
    	fac[0]=1;
    	for (int i=1;i<n;++i)
    		fac[i]=fac[i-1]*i%mo;
    	ifac[n-1]=qpow(fac[n-1]);
    	for (int i=n-2;i>=0;--i)
    		ifac[i]=ifac[i+1]*(i+1)%mo;
    	powd[0]=1;
    	for (int i=1;i<=LEN;++i)
    		powd[i]=powd[i-1]*D%mo;
    	for (int b=0;b<=LEN;++b){
    		for (int a=0;a<D;++a){
    			ll t=a*powd[b]%mo,p=1;
    			for (int i=0;i<n;++i,p=p*t%mo,t=(t-1+mo)%mo)
    				g[b][a][i]=p*ifac[i]%mo;
    		}
    		for (int i=0;i<n;++i){
    			sg[b][0][i]=0;
    			for (int a=1;a<D;++a)
    				sg[b][a][i]=(sg[b][a-1][i]+(T[a]?g[b][a][i]:0))%mo;
    		}
    	}
    	for (int i=0;i<n;++i){
    		input(L[i]),input(R[i]);
    		R[i]+=1;
    	}
    	dfs(0,1);
    	ans=(ans%mo+mo)%mo;
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    总结

    组合数的计数问题,可以考虑通过组合数的递推关系来计算。

  • 相关阅读:
    T3java核心API基础类
    java字符编码
    Servlet 1
    T2java面向对象
    T1java语言基础
    Mac OS mysql数据库安装与初始化
    java多线程中注入Spring对象问题
    T4java核心API集合类
    The first day Teddy
    Spring第二节 注入依赖
  • 原文地址:https://www.cnblogs.com/jz-597/p/12358380.html
Copyright © 2011-2022 走看看