标签:
题目:每个人有1~9的编号,有两个门,一帮人能进门,当且仅当这些人的编号之和与门的编号同余。问把这些人安排到两个门有多少种方法。
思路:比赛的时候有点脑抽,想了好久才发现是个简单dp。。。。只需要统计每个编号的人数,然后dp[i][j]表示前i个编号的人组成j同余的方案数,然后由于每种人选9的倍数个是无影响的,所以每种选择实际上是选x个,x+9,x+18……个,那么转移状态的时候需要计算n个数里选x个,x+9,x+18……个的方案数之和,这个怎么计算呢?利用一个递推式C[n][k]=C[n][k-1]*(n-k)/(k+1)(除法要用逆元代替),可以在O(n)的复杂度内一次得到所有这些值,计算的时候加到相应的组里就好了。
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio(false);std::cin.tie(0) #define pb push_back #define PB pop_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.00000001 #define IINF (1<<30) typedef long long ll; typedef long long LL; typedef long long lint; typedef pair<int,int> pii; typedef pair<ll,ll> P; const int mod=258280327; const int maxv=1e5+300; inline ll qpow(ll x,ll p){ ll ans=1; while(p>0){ if(p&1){ ans=(ans*x)%mod; } x=(x*x)%mod; p>>=1; } return ans; } ll inv[maxv]; void init(){ inv[1]=1; for(int i=2;i<maxv;i++){ inv[i]=qpow(i,mod-2); } } ll dp[15][15]; int cnt[15]; int T; int n,a,b; ll tol=0; ll CC[10]; int main(){ ////////freopen("/home/files/CppFiles/TeamContest/in","r",stdin); init(); cin>>T; while(T--){ memset(dp,0,sizeof dp); memset(cnt,0,sizeof cnt); tol=0; scanf("%d%d%d",&n,&a,&b); for(int i=0;i<n;i++){ int x; scanf("%d",&x); cnt[x]++; tol+=x; } if((a+b)%9!=tol%9){ if(tol%9==a%9||tol%9==b%9){ if(a==b){ cout<<2<<endl; continue; } cout<<1<<endl; continue; }else{ cout<<0<<endl; continue; } } dp[0][0]=1; for(int i=1;i<=9;i++){ memset(CC,0,sizeof CC); ll Cn=1; for(int k=0;k<=cnt[i];k++){ CC[k%9]=(CC[k%9]+Cn)%mod; Cn=(Cn*(cnt[i]-k)%mod*inv[k+1])%mod; } for(int j=0;j<=min(cnt[i],8);j++){ for(int k=0;k<9;k++){ dp[i][(k+i*j)%9]=(dp[i][(k+i*j)%9]+dp[i-1][k]*(CC[j])%mod)%mod; } } } if(a==9) a=0; cout<<dp[9][a]<<endl; } return 0; }
标签:
原文地址:http://www.cnblogs.com/Cw-trip/p/4731559.html